Dimension Conversions in Arturo

Here is a table of dimensions used by Arturo, expressed as a table in the Nim language:

arturo/src/vm/values/custom/quantities/definitions.nim at master · arturo-lang/arturo · GitHub

It's a pretty impressive compendium. And there's also some "heavily macro-driven" Nim code that implements conversion operations:

@drkameleon cites Frink as the main inspiration for this, though assembling the table and conversion code was original work on the order of months.

Why Not Use Arturo For The Table...?

The table is Nim code...and as such, beholden to having a lot of repetition and commas.

So of course my first question was "why not express it as Arturo, and then spit out the Nim table as part of the build process"?

Instead of:

#-----------------------------------------------------------------------
# Length units (base: m)
#-----------------------------------------------------------------------
#       name  symbol  prefix?    definition      aliases
#-----------------------------------------------------------------------
defUnit "in",  "in", false,  "127:5000 m",      "inch", "inches"
defUnit "ang",  "Å", false,  "1:10000000000 m", "angstrom", "angstroms"
defUnit "px",  "px", true,   "1:96 in",         "pixel", "pixels"

It could be reduced down to something more like this:

    === LENGTH (base: m) ===

     in [127:5000 m] inch inches
    ang [1:10000000000 m] angstrom angstroms ("Å")
   */px [1:96 in] pixel pixels

The meaning of "prefix?" is actually "prefixable?" that a type can have a prefix. e.g. pixels is prefixable because you can say Mpx for "megapixels". So I think some prefixing decoration on the type is a nice way to convey this. I just threw in */px as an example--though I gather that in Arturo that would be *\px

Though when I first saw some isolated instance of the colon notation I didn't realize it was a ratio. Ren-C could do this as a CHAIN! or as a PATH!. I think my first instinct would have been path:

     in [127/5000 m] inch inches
    ang [1/10000000000 m] angstrom angstroms ("Å")
   *.px [1/96 in] pixel pixels

But it actually looks a bit better with the colons I think.

Some notation would be nice for the pluralization. It's one of the places where the FUSED! proposal might come in handy:

     in [127:5000 m] inch{es}
    ang [1:10000000000 m] angstrom{s} ("Å")
   */px [1:96 in] pixel{s}

You can use tuples or paths, but it's not quite as neat...although the paths aren't terrible:

     in [127:5000 m] inch.es
    ang [1:10000000000 m] angstrom.s ("Å")
   */px [1:96 in] pixel.s

     in [127:5000 m] inch/es
    ang [1:10000000000 m] angstrom/s ("Å")
   */px [1:96 in] pixel/s

Incidentally, I think the conversion of types is cool, but I do not think doing it implicitly is wise. If I have a variable that I believe is in meters, and somehow a quantity in yards like distance comes along, and I say num_meters: distance + num_meters, the idea that the first argument dictates the type of the result is one of those things that seems like it would yield undesirable surprise behaviors

I'd rather say num_meters: (conform 'm distance) + num_meters or num-meters: (conform num_meters distance) + num_meters and get a type error otherwise. One of the big selling points of dimensional analysis is that it brings coherence and safety where there is otherwise incoherence, and if you start throwing in implicit conversions then you're diminishing that benefit.

Saying "the rule is it's the type of the first argument" for the operators may seem learnable, and people who get what they don't want can say "oh, I'll fix it" and then make sure all their calculations put what they want first. But I think it's a slippery slope. e.g. let's say someone writes a routine that takes three arguments, and they make a temporary out of the second input and accumulate something into it, then after some snakey code with the other values you get a kind of arbitrary result.

But this is my type-safety bias for programming at scale. What's appropriate for a calculator app is entirely different. I just don't think languages are calculator apps.

Alternately, conform could be a dialect that does conversions, and not the + operator, e.g. num_meters: conform [num_meters + distance] where it would have rules to pick the first thing, convert it, then delegate to + to do the work, with plain + being type-safe.

Further on this, the most comprehensive units list I know of is that used by the Frink language (which is worth looking at!): http://frinklang.com/frinkdata/units.txt

1 Like