Stream: ideas

Topic: "calling numbers"


view this post on Zulip Richard Feldman (Oct 17 2022 at 04:31):

so there's currently never a valid reason to "call" a number, e.g. 5 foo is always an error

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:32):

but what if we made it so that 5 foo was syntax sugar for 5 |> foo?

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:32):

that would let you write things like:

5 seconds
3 minutes

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:33):

and if you have functions named seconds and minutes in scope, they'd get called passing those numbers

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:34):

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

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:36):

seems like it could be useful with:

view this post on Zulip Richard Feldman (Oct 17 2022 at 04:36):

anyone have thoughts on this?

view this post on Zulip Anton (Oct 17 2022 at 06:39):

I have some bad "hidden magic" feelings but also feel like it may be worth it. Could we get some confusing operator precedence problems?

view this post on Zulip Anton (Oct 17 2022 at 06:42):

desugaring 5 foo to ( 5 |> foo ) seems like it would prevent any problems.

view this post on Zulip Luke Boswell (Oct 17 2022 at 06:44):

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.

view this post on Zulip Brian Carroll (Oct 17 2022 at 07:27):

I don't like this. I think adding a special case like this makes the language strictly more complex and confusing.

view this post on Zulip Martin Stewart (Oct 17 2022 at 07:30):

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.

view this post on Zulip lue (Oct 17 2022 at 14:28):

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)

view this post on Zulip Brendan Hansknecht (Oct 17 2022 at 14:54):

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:

view this post on Zulip Georges Boris (Oct 17 2022 at 16:40):

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

view this post on Zulip Zeljko Nesic (Oct 18 2022 at 01:25):

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"

view this post on Zulip jan kili (Oct 18 2022 at 02:57):

@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 == ""

view this post on Zulip Brendan Hansknecht (Oct 18 2022 at 03:13):

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.

view this post on Zulip Arbil (Oct 18 2022 at 04:50):

Brendan Hansknecht said:

As a quick comment, i think it is very important to consider the parsing:

5km has good pasting an you can write convertToMile 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!

view this post on Zulip Georges Boris (Oct 18 2022 at 08:31):

would units desugar to phantom types? or would it live in a new layer of abstraction?

view this post on Zulip Zeljko Nesic (Oct 18 2022 at 13:44):

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.

view this post on Zulip Brendan Hansknecht (Oct 18 2022 at 14:07):

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.

view this post on Zulip Brendan Hansknecht (Oct 18 2022 at 14:08):

Also, being tags means matching on units would be fast, unlike strings.

view this post on Zulip Brendan Hansknecht (Oct 18 2022 at 14:10):

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.

view this post on Zulip jan kili (Oct 18 2022 at 14:47):

I feel like tags are Roc's intended mechanism for attaching context - otherwise why aren't Errs strings?

view this post on Zulip Zeljko Nesic (Oct 18 2022 at 15:52):

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.

view this post on Zulip Zeljko Nesic (Oct 18 2022 at 15:52):

But that is far from "calling numbers". :)

view this post on Zulip Qqwy / Marten (Oct 22 2022 at 19:41):

We already allow e.g. 5u64. What about extending this to be usable for user-types instead?

view this post on Zulip Richard Feldman (Oct 22 2022 at 20:25):

I have a proposal for that from awhile back: https://docs.google.com/document/d/1xBOG185KDsP57O6QlC7KlIihNbWX5V8ZUzqDVXwV67g/edit?usp=drivesdk

view this post on Zulip Kevin Gillette (Oct 26 2022 at 07:02):

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:

  1. There's an ambiguity (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.
  2. User-defined constructors could shadow types, i.e. if you your own 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