[Python-ideas] SI scale factors in Python

Ivan Levkivskyi levkivskyi at gmail.com
Fri Aug 26 07:49:51 EDT 2016


On 26 August 2016 at 13:01, Steven D'Aprano <steve at pearwood.info> wrote:

> On Fri, Aug 26, 2016 at 07:35:36AM +0200, Pavol Lisy wrote:
> > On 8/25/16, Ken Kundert <python-ideas at shalmirane.com> wrote:
> >
> > [...]
> >
> > > Just allowing the units to be present, even it not
> > >
> > > retained, is a big advantage because it can bring a great deal of
> clarity to
> > > the
> > > meaning of the number. For example, even if the language does not flag
> an
> > > error
> > > when a user writes:
> > >
> > >     vdiff = 1mV - 30uA
> >
> > It reminds me: "Metric mishap caused loss of NASA's Mars Climate
> > orbiter. It could be nice to have language support helping to avoid
> > something similar.
>

[snip]


> Take v = s/t (velocity equals distance over time). If I write v = s
> because it is implicitly understood that the time t is "one":
>
>     s = 100 miles
>     v = s
>
> Should v be understood as 100 miles per hour or 100 miles per second or
> 100 miles per year? That sort of ambiguity doesn't come up in circuit
> design, but it is common elsewhere.
>

If one writes this as

from units import m, s, miles

s = miles(100)
v: m/s = s

This could be flagged as an error by a static type checker.

Let me add some clarifications here:
1. By defining __mul__ and __truediv__ on m, s, and other units one can
achieve
the desirable semantics

2. Arbitrary (reasonable) unit can be described by a tuple of 7 rational
numbers
(powers of basic SI units, m/s will be e.g. (1, -1, 0, 0, 0, 0, 0)), if one
wants also
non SI units, then there will be one more float number in the tuple.

3. It is impossible to write down all the possible overloads for operations
on units,
e.g. 1 m / 1 s should be 1 m/s, 1 m/s / 1 s should be 1 m/s**2,
and so on to infinity. Only finite number of overloads can be described
with PEP 484 type hints.

4. It is very easy to specify all overloads with very basic dependent types,
unit will depend on the above mentioned tuple, and multiplication should be
overloaded
like this (I write three numbers instead of seven for simplicity):

class Unit(Dependent[k,l,m]):
    def __mul__(self, other: Unit[ko, lo, mo]) -> Unit[k+ko, l+lo, m+mo]:
        ...

5. Currently neither "mainstream" python type checkers nor PEP 484 support
dependent types.

6. For those who are not familiar with dependent types, this concept is
very similar to generics.
Generic type (e.g. List) is like a "function" that takes a concrete type
(e.g. int) and "returns" another
concrete type (e.g. List[int], lists of integers). Dependent types do the
same, but they allowed to
also receive values, not only types as "arguments". The most popular example
is matrices of fixed size n by m: Mat[n, m]. The matrix multiplication then
could be overloaded as

class Mat(Dependent[n, m]):
    def __matmul__(self, other: Mat[m, k]) -> Mat[n, k]:
        ...

7. I like the formulation by Nick, if e.g. the library circuit_units
defines sufficiently many overloads,
then it will safely cover 99.9% of use cases *without* dependent types. (An
operation
for which type checker does not find an overload will be flagged as error,
although the operation might be correct).

--
Ivan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160826/86e2d154/attachment.html>


More information about the Python-ideas mailing list