MRAB writes: > When you divide 2 values, they could have the same or different units, > but must have the same colours. The result will have a combination of > the units (some might also cancel out), but will have the same colours. > > # "amp" is a unit, "current" is a colour. > # The result is a ratio of currents. > 6 amp current / 2 amp current == 3 current
I don't understand why a ratio would retain color. What's the application? For example, in circuit analysis, if "current" is a color, I would expect "potential" and "resistance" to be colors, too. But from I1*R1 = V = I2*R2, we have I1/I2 = R2/R1 in a parallel circuit, so unitless ratios of color current become unitless ratios of color resistance. Furthermore that ratio might arise from physical phenomena such as temperature-varying resistance, and be propagated to another physical phenomenon such as the deflection of a meter's needle. What might color tell us about these computations? (Note: I'm pretty sure that MRAB didn't choose "current" as a parallel to "resistance". Nevertheless, the possibility of propagation of values across color boundaries to be necessary and I don't see how color is going to be used.) My own take is that units specify possible operations, ie, they are nothing more than a partial specification of a type. Rather than speculate on additional attributes that might useful in conjunction with units, we should see if there are convenient ways to describe the constraints that units produce on behavior of types. Ie, by creating types VoltType(UnitType), AmpereType(UnitType), and OhmType(UnitType), and specifying the "equation" VoltType = AmpereType * OhmType on those types, the __mul__ and __div__ operators would be modified to implement the expected operations as a function of UnitType. That is, there would be a helper impose_type_expression_equivalence() that would take the string "VoltType = AmpereType * OhmType" and manipulate the derived type methods appropriately. One aspect of this approach is that we can conveniently derive concrete units such as V = VoltType(1) # Some users might prefer the unit # Volt, variable V, thus "VoltType" mA = AmpereType(1e-3) # SI scale prefix! kΩ = OhmType(1e3) # Unicode! <wink/> They don't serve the OP's request (he *doesn't* want type checking, he *does* want syntax), but I prefer these anyway: 10*V == (2*mA)*(5*kΩ) Developers of types for circuit analysis derived from UnitType might prefer different names that reflect the type being measured rather than the unit, eg Current or CurrentType instead of AmpereType. There is no problem with units like Joule (in syntax-based proposals, it collides with the imaginary unit) and Kelvin (in syntax-based proposals, it collides with a non-SI prefix that nevertheless is so commonly used that both the OP and Steven d'Aprano say should be recognized as "kilo"). Another advantage (IMHO) is that "reified" units can be treated as equivalent to "real" (aka standard or base) units. What I mean is that New York is not approximately 4 Mm from Los Angeles (that would give most people a WTF feeling), it's about 4000 km. While I realize programmers will be able to do that conversion, this flexibility allows people to use units that feel natural to them. If you want to miles and feet, you can define them as ft = LengthType(12/39.37) # base unit is meter per SI mi = 5280*ft very conveniently. Using this approach, Ken's example that Nick rewrote to use type hinting would look like this: from circuit_units import uA, V, mV, kOhm, u_second, VoltType us = u_second # Use project convention. # u_second = SecondType(1e-6) # A gratuitous style change. expected: VoltType # With so few declarations, # I prefer "predeclaration". # There is no millivolt type, # so derived units are all # consistent with this variable. # A gratuitous style change. for delta in [-0.5*uA, 0*uA, 0.5*uA]: # uA = AmpereType(1e-6) # I dislike [-0.5, 0, 0.5]*uA, # but it could be implemented. input = 2.75*uA + delta wait(1*us) # The "1*" is redundant. expected = (100*kOhm)*input # kOhm = OhmType(1e3) tolerance = 2.2*mV # mV = VoltType(1e-3) fails = check_output(expected, tolerance) print('%s: I(in)=%rA, measured V(out)=%rV, expected V(out)=%rV, diff=%rV.' % ( 'FAIL' if fails else 'pass', input, get_output(), expected, get_output() - expected )) Hmm: need for only *one* variable declaration. This is very much to my personal taste, YMMV. The main question is whether this device could support efficient computation. All of these units are objects with math dunders that have to dispatch on type (or else they need to produce "expression Types" such as Ampere_Ohm, but I don't think type checkers would automatically know that Volt = Ampere_Ohm). This clearly can't compare to the efficiency of NewType(float). But AIUI, one NewType(float) can't mix with another, which is not the behavior we want here. We could do VoltType = NewType('Volt', float) AmpereType = NewType('Ampere', float) WattType = NewType('Watt', float) def power(potential, current): return WattType(float(Volt)*float(Ampere)) but this is not very readable, and error-prone IMO. It's also less efficient than the "zero cost" that NewType promises for types like User_ID (https://www.python.org/dev/peps/pep-0484/#newtype-helper-function). I suppose it would be feasible (though ugly) to provide two implementations of VoltType, one as a "real" class as I propose above, and the other simply VoltType = float. The former would be used for type checking, the latter for production computations. Perhaps such a process could be automated in mypy? A final advantage is that I suppose that it should be possible to implement "color" as MRAB proposes through the Python type system. We don't have to define it now, but can take advantage of the benefits of "units as types" approach immediately. I don't know how to implement impose_type_expression_equivalence(), yet, so this is not a proposal for the stdlib. But the necessary definitions by hand are straightforward, though tedious. Individual implementations of units can be done *now*, without change to Python, AFAICS. Computational efficiency is an issue, but one that doesn't matter to educational applications, for example. Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/