As part of the FormaK library, I'd like to introduce the concept of physical units into the UI this would allow for checking that things like the following are correct (every element added together ends up in units of velocity)
vel = accel * dt + snap * (dt ^2) / 2
and something like the following is incorrect (there's a missing dt element)
position = velocity + position
I'd also like to approach the integration of units in a "zero-cost abstraction" way where a user doesn't pay for what they don't use but they do get increasing utility with increasing effort to use features in the library. This means that a gradual approach like is used in Mypy/Pep 484 for typing and checking Python programs would be ideal. In fact, can we take that approach to develop a physical units system based on Python's types?
# ... some hacky code ... class UnitMeta(type): def __getitem__(cls, key: Tuple[int, int]): meters, seconds = key env = dict(UnitImpl.__dict__) env["meters"] = meters env["seconds"] = seconds Type = type("Unit", (UnitImpl,), env) return GenericAlias(Type, key) class Unit(UnitImpl, metaclass=UnitMeta): def __add__(self, rhs: Unit): return super().__add__(rhs) dt = Unit[0, 1]("dt") # Unit[0,1] accel = Unit[1, -2]("accel") # Unit[1,-2] jerk = Unit[1, -3]("jerk") # Unit[1,-3] print("a", accel) print("dt", dt) print("a * dt", accel * dt) vel = accel * dt + jerk * (dt * dt) / 2 print("vel", vel) position = Unit[1, 0]("position") # Unit[1,0] position = ( vel + position ) # Expect to fail here (velocity not the same units as position)
python3 -m pip install -U mypy typing; mypy physical_units.py
Mypy "error" message:
Success: no issues found in 1 source file
Unfortunately, at this time I've not been able to get to a point where I can get Mypy to fail with crossing up meters/seconds units.
The Runtime Checking Future¶
As a final step to be most useful for FormaK, I'd like to be able to check at runtime so we can perform the checking as part of model validation. The tool for the job is Typeguard.
Originally I'd expect to be able to use Mypy, but it's pretty set on being a command line tool for files according to Guido Van Rossum:
... We're reluctant to make mypy importable and callable from other code, because mypy's primary usage is through the command line, targeted at whole programs, and we often make big changes to the internals that would break external invocations. Think of mypy as a linter ...
However, that redirected me to Dynamic PEP 484 type checking without code changes? and that pointed me to Typeguard [commit, User Guide]
Taking another look at the code example above, this time with Typeguard
This simple example of units focuses on two units (distance, time) and needs something extra to get working via Mypy type checking. If you'd like to contribute this or another feature to the FormaK project, reach out to me on Twitter @bebaskin or reach out on the proposal issue on Github.