Btw, when using (:::) this way, it's nice to set the operator precedence to something lower than 9 so you can say `2 * 5 + 3 ::: radians` without having to put brackets around the numbers. I think `infix 5 :::` gives the right precedence; Looking at the core docs ( https://github.com/elm-lang/core/blob/master/src/Basics.elm ), (+) and (-) are 6, and (==), (/=), etc are 4.
On Friday, July 29, 2016 at 12:50:24 PM UTC-5, John Bugner wrote: > > I like the clever use of the (:::) function to make its function argument > (somewhat) look like a type. > > On Friday, July 29, 2016 at 11:48:26 AM UTC-5, Anton Lorenzen wrote: >> >> I just created a small library for this: >> https://github.com/anfelor/elm-units >> >> It allows for time to be used like this: >> Units.Time.every (30 ::: milliseconds) Tick >> >> Am Donnerstag, 28. Juli 2016 20:42:44 UTC+2 schrieb John Bugner: >>> >>> In the time library ( >>> package.elm-lang.org/packages/elm-lang/core/4.0.3/Time ), there are >>> many functions under the "Units" heading. Under the function `millisecond`, >>> it says: >>> >Units of time, making it easier to specify things like a half-second >>> (500 * millisecond) without remembering Elm’s underlying units of time. >>> I find this underwhelming, because nothing forces me to use these unit >>> functions. I could write `every 5 toMsg` and the compiler will not stop me. >>> How long of a time am I actually specifying? Probably 5 milliseconds, >>> because I infer that it's probably using the same unit as JavaScript, but I >>> can't know for sure. A beginner might think 5 seconds, because it's the >>> unit that has no prefixes, or 5 minutes if 5 minutes seems appropriate to >>> what the application does. This is very bad; There should never be doubt >>> about what unit is being used, and the compiler should enforce this unit >>> correctness. Currently, Elm can't do this, because `Time` is just an alias >>> for `Float`. >>> >>> To prevent this kind of error, I propose a new language construct that I >>> call a "unit type". It would have the following properties: >>> (1) A definition that would look (very roughly) like this: >>> ``` >>> type unit Time as Float >>> = Second >>> | Millisecond == Second 0.0001 >>> | Nanosecond == Millisecond 0.0001 >>> | Minute == Second 60 >>> | Hour == Minute 60 >>> | Day == Hour 24 >>> | Year == Day 365 >>> ``` >>> >>> The point of the definition is to: >>> (a) Provide an easy way to define different units that measure the same >>> thing (in this case, time, but you could do the same thing for >>> length/height/depth in either metric or US imperial units) as constructors. >>> (b) Tell how they are related to eachother. (A minute is 60 seconds, an >>> hour is 60 minutes, etc.) The compiler would check that all relations of >>> the constructors eventually flow to a single base unit (in this case, >>> `Second`). A cycle would be disallowed. >>> (c) Tell what type the unit is based on (in this case, `Float`). >>> (Perhaps non-number types would be disallowed.) >>> >>> (2) Given that `every` still has the same signature: `Time -> (Time -> >>> msg) -> Sub msg`, `every 5 toMsg` would cause a compile-time error, because >>> the types don't match; The function expects a number with a `Time` unit, >>> but receives `5`, which is just a raw number (a number without any unit). >>> (3) When writing a function that has a parameter of type `Time`, pattern >>> matching only matches the base unit constructor, not every constructor like >>> a normal (data/enum) type. The compiler would automatically convert the >>> other units to the base unit with the conversions that the programmer >>> provided in the definition. >>> (4) Comparison, addition, and subtraction would be automatically >>> implemented for the type, so two times of any combination of constructors >>> could be compared, added, or subtracted with ease. `(Minute 5) - (Second >>> 20) == (Millisecond 280000)` would "just work". "Time + Float" (or "Time + >>> Length") would cause a compile-time error. >>> (5) Perhaps compound unit types like "Time^2" would be supported, so a >>> "Time * Time" would yield "Time^2", "Time * Float" would yield "Time", and >>> "Time * Length" would yield just that: "Time * Length". ("Force" would be >>> an alias of "Mass * Length / (Second^2)".) >>> >>> I know that this is a very radical proposal, (I don't know of any >>> language that has a feature like this.) but I bring it up anyways, because >>> although it's been 18 years since the mars probe crashed because of a unit >>> error (one module assumed that the number it was getting was in metric >>> units, and another assumed that it was getting it in US imperial units) ( >>> https://en.wikipedia.org/wiki/Mars_Climate_Orbiter ), I'm amazed that >>> since then, programming languages have done nothing to prevent this kind of >>> error from happening again, besides just admonishing programmers to be >>> careful. (As if the NASA programmers at the time weren't already trying to!) >>> >>> Letting `5` be a legal unitless `Time` value is just as silly and >>> dangerous as letting `bool a = 2;` be a legal statement in C. data/enum >>> types prevent this from happening to `Bool` in Elm, and unit types could >>> prevent the same kind of thing from happening to `Time`. >>> >>> Questions, comments, related thoughts, etc are welcome. >>> >>> -- You received this message because you are subscribed to the Google Groups "Elm Discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
