Open rtfeldman opened 3 years ago
My first thought for how to make this nicer: introduce a new constraint in type checking along the lines of "is a valid number" and give a helpful type error if it doesn't unify to a known-valid Num
type.
However, the more I think about this, the more complicated it seems like it would be. For example, here's an equivalent one done with type aliases:
X : {}
Z a : Num a
test : Z X
test = 5
How would we propagate the "must be number-friendly" constraint? We'd presumably have to do something like what Elm does with number
, except behind the scenes instead of having it be reflected in the type variable name.
Hidden constraints like that can create other edge cases; e.g. if I have a package which publishes a Foo a
type, and I change it so that a
ends up needing to be number-friendly, that would need to be a breaking change to the package even though the types didn't change, because it could break someone's build.
Alternative ideas:
Num
arguments as Num *
- that is, I64
by default. Similarly with Float
and Float *
. That's not great; if a mistake was made, we should report it!Another idea: all unrecognized Num
arguments are the equivalent of U0
- that is, a number that's always zero at runtime, which is to say, effectively as useful as {}
. The only value you can give to satisfy any of these is0
, so if you did this...
test : Num {}
test = 5
...it would be handled the same way as any number literal that's too big for its type, like trying to assign 300
to a U8
.
This would also be a separately useful language feature, because it would allow for "typed zeroes" which work like Rust's PhantomData
type - they're a parameterized type which is always zero-sized (and thus optimized away at compile time, never adding runtime overhead). Right now Roc doesn't have a way to do that!
Incidentally, all the usual Num
operations would work as normal, e.g. addition:
zero : Num [ Foo, Blah Str ]
zero = 0
stillZero : Num [ Foo, Blah Str ]
stillZero = zero + zero
...although behind the scenes, since these would be zero-sized types, we'd just monomorphize Num.add
on unrecognized Num
types to a function that ignores its arguments and always returns 0. Same for other such numeric functions.
If I write this, everything is fine:
However, if I write this...
...monomorphization panics: