so there's currently never a valid reason to "call" a number, e.g. 5 foo is always an error
but what if we made it so that 5 foo was syntax sugar for 5 |> foo?
that would let you write things like:
5 seconds
3 minutes
and if you have functions named seconds and minutes in scope, they'd get called passing those numbers
obviously you can already write seconds 5 or 5 |> seconds but this looks a lot nicer, seems very easy to learn, and isn't ambiguous with any other syntax
seems like it could be useful with:
5 mb)300 usd)anyone have thoughts on this?
I have some bad "hidden magic" feelings but also feel like it may be worth it. Could we get some confusing operator precedence problems?
desugaring 5 foo to ( 5 |> foo ) seems like it would prevent any problems.
This sounds like a really neat idea. Would this be restricted to unspecified numbers, or maybe this is another numeric type? I imagine if you used 5u8 which has type U8 then it may not be compatible with the signature of seconds. Or is this a feature we can now do using abilities, like seconds : number -> Seconds number where number has Num? I like the idea of having function calls like calculateDistance (5 metres) (10 seconds) the brackets don't feel out of place.
I don't like this. I think adding a special case like this makes the language strictly more complex and confusing.
I second Brian's opinion. Additionally I like qualifying all my function calls which means this doesn't add much in terms of readability. 2 Time.seconds doesn't look better than Time.seconds 2 imo.
Also, units are more important and give more context than values → better placed before
Same reason why you don't write
empty.Array
field.object
[ { key = "person", value = 200 }, ... ] |> Dict.fromList -- for construction
map
=
\elementChange \array
...
:
(a -> b) -> (Array a -> Array b)
I think something like this would need to be constrained. Like if you have a tag that takes a single constant primitive value, you can prepend it to the value.
So instead of an arbitrary function -73 Num.abs, it would only be for tags in the form Distance a : [ Meters (Num a), Feet (Num a) ] then you could do 73 Meters or something. Not really sure the exact syntax. Would maybe make sense to expand to other primitive constants "person@email.com" Email. Though this also skips any form of parsing or validation....so :shrug:
I think it would be good to see this in a small example module. In isolation it seems more confusing than helpful. Like Martin said - with qualified functions it loses the only upside of readability.
Game.shoot 2 Game.bullets
Use cases for it break the patterns of less surprising programming. So they are anti-patterns. :upside_down:
But, I second this: allow units.
x : Num *
x = 5whateva
expect Num.unit x == "whateva"
dist : Num *
dist = 1029334.31km
expect Num.unit dist == "km"
@Zeljko Nesic should we upgrade from strings to something more expressive? I don't know what kind of tags, but this seems insufficient: expect Num.unit 5 == ""
As a quick comment, i think it is very important to consider the parsing:
5km has good pasting an you can write convertToMile 5km
5 km has bad parsing. You have to write convertToMiles (5 km)
Also, i really like the idea of these somehow materializing to tags such that i can pattern match on the unit. Have the implementation of convertToMiles match on km, m, cm, miles, feet, etc.
Brendan Hansknecht said:
As a quick comment, i think it is very important to consider the parsing:
5kmhas good pasting an you can writeconvertToMile 5km
Yeah. Fsharp does 5<km> and its units of measure implementation has been SOTA for a long time. High time some language improved on that!
would units desugar to phantom types? or would it live in a new layer of abstraction?
JanCVanB said:
Zeljko Nesic should we upgrade from strings to something more expressive? I don't know what kind of tags, but this seems insufficient:
expect Num.unit 5 == ""
Nop, I would keep it stringly typed. Why?
1) Units are not tags. Tags are values, units are contextual attachments.
2) You can always develop elaborate wrappers around this, that would spit numbers wrapped in your system of tags. expect Volume.unit 4.2m3 == CubicMeter
Some units you might not care about, but for those that you care about you ought to have your system.
I think units have to be more than just strings, they have to be part of the type system. Otherwise, you get a lot of complications around defining functions. If they are part of the type system because they have to match up between caller and callee, they essentially are tags.
Also, being tags means matching on units would be fast, unlike strings.
If units are just for context/annotation, I think they would be better suited as either comments, context in function names doSomethingMeters val, or a debug/context record field.
I feel like tags are Roc's intended mechanism for attaching context - otherwise why aren't Errs strings?
Fair enough.
Just then I am not sure how you would use it / define it's type, but if there is a way sounds powerful indeed.
But that is far from "calling numbers". :)
We already allow e.g. 5u64. What about extending this to be usable for user-types instead?
I have a proposal for that from awhile back: https://docs.google.com/document/d/1xBOG185KDsP57O6QlC7KlIihNbWX5V8ZUzqDVXwV67g/edit?usp=drivesdk
What would be special about numbers in this case? If accepted for numbers, couldn't it (grammatically) work just as well for strings and any other literal? (certainly I'm not proposing we do so).
Could it be more constrained to just units? For example, if I could specify that a FileSize type has mib, tb, b, etc constructors (functions which accept some number type and return another type), then 5b (no spaces) could be treated as equivalent to (5 |> b) as you say. The lack of spacing means that where, as called out by others, allocate 2 mb would be parsed as allocate (2) (mb), allocate 2mb would mean allocate (2 |> mb).
Some downsides of the lack of spacing is:
0b11 a binary literal for the value 3, or is it 0 passed to the unit constructor b11?). This can be resolved by constraining unit constructors such that they cannot have numbers in their identifier.dec constructor, then the compiler (and reader) could not know what 10dec means without looking at the imports.Last updated: Jun 16 2026 at 16:19 UTC