Stream: beginners

Topic: syntax: whitespace vs comma for items


view this post on Zulip noonien (Nov 15 2023 at 08:59):

From what I can tell, Roc function call arguments are separated with whitespace, however, arguments in a function definition, and items in lists and records are separated with commas. Why are these different? I understand that not having commas in definitions, lists and records would be more annoying because there would need to be parens instead, but why not keep everything uniform and have commas for function call arguments as well?

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:38):

great question! It's mainly because of what happens when function calls happen inside lists - for example, in our static site generator for roc-lang.org:

https://github.com/roc-lang/roc/blob/7b1f2d2ac1f6e0b7a283cfbe932318f37a0a6861/www/wip_new_website/main.roc#L104

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:39):

today, this can be:

 a [id "home-link", href "/", title "Roc"] [rocLogo]

...but if we required commas to separate arguments in function calls, then it would have to be this instead:

 a [(id "home-link"), (href "/"), (title "Roc")] [rocLogo]

...because the first way would become ambiguous; we couldn't tell if [id "home-link", href meant "I want to call id passing "home-link" and href in the usual comma-separated way" or "I want to call id passing "home-link" and then href begins the next element in the list"

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:40):

that said, I actually think it's an interesting idea to revisit that decision

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:42):

originally I assumed that pattern would come up often (because it came up all the time in Elm, specifically for DSLs like this) but to date that one specific DSL is the only place I've seen this come up :big_smile:

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:42):

actually tuples would be another place - I don't have an example to link to offhand, but for example (foo bar, baz blah) would have to become ((foo bar), (baz blah))

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:44):

we haven't tried it, but another thing that's possible in the HTML DSL is to use optional record fields - in which case the above code could be:

 a { id: "home-link", href: "/", title: "Roc" } [rocLogo]

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:45):

although the same consideratoin happens for the list of child nodes - e.g. https://github.com/roc-lang/roc/blob/7b1f2d2ac1f6e0b7a283cfbe932318f37a0a6861/www/wip_new_website/main.roc#L109

would go from this:

            div [id "top-bar-links"] [
                a [href "/wip/tutorial"] [text "tutorial"],
                a [href "/wip/install"] [text "install"],
                a [href "/wip/community"] [text "community"],
                a [href "/wip/docs"] [text "docs"],
                a [href "/wip/donate"] [text "donate"],
            ],

...to this:

            div [id "top-bar-links"] [
                (a [href "/wip/tutorial"] [text "tutorial"]),
                (a [href "/wip/install"] [text "install"]),
                (a [href "/wip/community"] [text "community"]),
                (a [href "/wip/docs"] [text "docs"]),
                (a [href "/wip/donate"] [text "donate"]),
            ],

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:46):

also worth noting that the optional record fields design raises the design question of what to do with custom attributes (such as data- attributes) - might need a custom: field which takes a list of tuples or something

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:48):

all that said, I could see an argument for the extra commas being worth it for other reasons (e.g. readability, being able to pass ifs or whens as middle arguments without needing parens, etc.)

view this post on Zulip Richard Feldman (Nov 15 2023 at 11:53):

actually https://github.com/roc-lang/roc/blob/98df325d76b6b743c6b01f399bbbeaaf0d8108fa/www/wip_new_website/main.roc#L107 is a better example of the downside, would need to have parens across multiple lines if homeLink was moved ahead one spot:

        nav [ariaLabel "primary"] [
            (div [id "top-bar-links"] [
                (a [href "/wip/tutorial"] [text "tutorial"]),
                (a [href "/wip/install"] [text "install"]),
                (a [href "/wip/community"] [text "community"]),
                (a [href "/wip/docs"] [text "docs"]),
                (a [href "/wip/donate"] [text "donate"]),
            ]),
            homeLink,
        ],

so in general DSLs that wanted to use function calls to make the elements would get a lot more parens!

view this post on Zulip noonien (Nov 15 2023 at 12:04):

I see, that makes sense.
I'm looking over the syntactic design choices of programming languages and this particular choice in Roc seemed quite interesting, the lack of "uniformity" does seem to be massively outweighed by the improvements to succinctness.
Thanks for your reply!

view this post on Zulip Richard Feldman (Nov 15 2023 at 12:14):

absolutely, thanks for the question! :smiley:

view this post on Zulip Brendan Hansknecht (Nov 15 2023 at 15:46):

Personally, I would rather consider removing commas from the type definition than adding them to the function call site.

That said, I'm pretty sure that would lead to a mess and don't currently have a good alternative.

I guess we could remove commas and require parens around more complex types just like with nested function calls currently. I wonder how that would look.

view this post on Zulip Jacob (Nov 15 2023 at 16:04):

In this case. Could u just use a semicolon as the separator? Are the only conflicts because you want to use the same symbol for separation in lists?

view this post on Zulip Brendan Hansknecht (Nov 15 2023 at 16:07):

A few examples of instead changing the type to match the calling syntax. Tried to pick more complex looking signitures from current roc code:

Note, I left off parens around the output type given it is never ambiguous, but they could be added

update : (Dict k v) k ([Present v, Missing] -> [Present v, Missing]) -> Dict k v where k implements Hash & Eq
write : (List U8) Stream -> Task {} [TcpWriteErr StreamErr]
andMap : (Parser a) (Parser (a -> b)) -> Parser b
walkTryHelp : (List elem) state (state elem -> Result state err) Nat Nat -> Result state err

view this post on Zulip Brendan Hansknecht (Nov 15 2023 at 16:08):

Could u just use a semicolon as the separator? Are the only conflicts because you want to use the same symbol for separation in lists?

Yes, though I don't think many people would want different separates that they have to remember based on context.

view this post on Zulip Jacob (Nov 15 2023 at 16:09):

I do like the way lean has type annotations, very clean, but can get long sometimes. The parenthesis do make things very clear and organized though.

view this post on Zulip Jacob (Nov 15 2023 at 16:11):

Brendan Hansknecht said:

Could u just use a semicolon as the separator? Are the only conflicts because you want to use the same symbol for separation in lists?

Yes, though I don't think many people would want different separates that they have to remember based on context.

Yeah thats kinda annoying if all you're getting is that the call site has commas.

view this post on Zulip timotree (Nov 15 2023 at 20:15):

Another way to use commas for function calls would be to use f(x, y, z) syntax, which would be more familiar to people coming from imperative languages. Is there a benefit unique to functional programming that causes what seems like every functional language (Elm, Haskell, Lean, Roc, etc) to use f x y z syntax instead?

view this post on Zulip timotree (Nov 15 2023 at 20:17):

I suppose the juxtaposition syntax makes a lot of sense in curried languages. But Roc isn't curried

view this post on Zulip Richard Feldman (Nov 15 2023 at 20:24):

yeah Roc uses it because of its practical benefits (in terms of avoiding parentheses in DSLs like the above example), not because of consistency or conceptual elegance :big_smile:

view this post on Zulip Kevin Gillette (Nov 30 2023 at 03:36):

is unparenthesized if or when in the middle of a list a real win though? I'd be a bit concerned about readability in such a case, and in more recent years, I've tended to favor breaking down complex expressions into simpler ones by using intermediate variables.

Yes, naming things is hard, but also, to some extent, if it's hard to give a reasonable name, then it might not be the best way to achieve what you're trying to build.

I have found commas to be a poor visual delimiter for use in complex expressions... It's great for a list of numbers or a list of strings, or even a list of lists, but strings and lists have their own, stronger delimiters. In terms of function calls, more than a few times I've been confused about which tokens are arguments to a call, and which are elements in the list.

view this post on Zulip Kevin Gillette (Nov 30 2023 at 03:46):

These are certainly hard tradeoffs to make. I do rather appreciate the style choice of (f x) over f(x) when parens are needed to avoid ambiguity. I do greatly prefer writing f : (List x) y instead of f : List x, y because the former is more approachable and less visually ambiguous, and because non-trivial parameters will require parens anyways.

view this post on Zulip Kevin Gillette (Nov 30 2023 at 03:55):

Side note: I'd certainly be happy writing f : (List x), y but I'd probably parenthesize all trailing params if any of the preceding required it, just for balance and visual clarity, i.e. f : (List x) (y)

view this post on Zulip Richard Feldman (Nov 30 2023 at 15:19):

I think the upside is much more for function calls than if or when, e.g.

Richard Feldman said:

actually https://github.com/roc-lang/roc/blob/98df325d76b6b743c6b01f399bbbeaaf0d8108fa/www/wip_new_website/main.roc#L107 is a better example of the downside, would need to have parens across multiple lines if homeLink was moved ahead one spot:

view this post on Zulip Notification Bot (Nov 30 2023 at 16:57):

2 messages were moved from this topic to #beginners > compiling for wasm by Brendan Hansknecht.

view this post on Zulip Kevin Gillette (Dec 01 2023 at 01:22):

hmmm. yeah, that's true. Some DSLs seem to benefit from this a lot, and for others, it may be more neutral either way


Last updated: Jul 06 2025 at 12:14 UTC