Stream: ideas

Topic: Comma Delimited Arguments when function is called


view this post on Zulip Brian Teague (Jan 25 2024 at 15:16):

Do you think making arguments comma delimited on function invocation just like lambda arguments makes this code easier to read / understand?

Current ROC syntax

when Dict.get graph current is
    Ok neighbors ->
        # filter out all seen neighbors
        filtered = List.keepIf neighbors (\n -> !(Set.contains seen n))

        # newly explored nodes are added to the FIFO queue
        newQueue = List.concat rest filtered

        # the new nodes are also added to the seen set
        newSeen = List.walk filtered seen Set.insert

        bfsHelper isTarget newQueue newSeen graph

    Err KeyNotFound ->
        bfsHelper isTarget rest seen graph

Comma Delimited ROC

when Dict.get graph, current is
    Ok neighbors ->
        # filter out all seen neighbors
        filtered = List.keepIf neighbors, \n -> !(Set.contains seen, n)

        # newly explored nodes are added to the FIFO queue
        newQueue = List.concat rest, filtered

        # the new nodes are also added to the seen set
        newSeen = List.walk filtered, seen, Set.insert

        bfsHelper isTarget, newQueue, newSeen, graph

    Err KeyNotFound ->
        bfsHelper isTarget, rest, seen, graph

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 15:51):

I think commas lead to weird precedence issues. You can't tell if something is a nested function call for a single arg or many args

Str.concat3 "something", Num.toStr data, "more data"

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 15:54):

One thing that was proposed before was to allow calling a function with a tuple if wanted instead of the individual args.

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 15:54):

I think it got stuck on parsing concerns/vagueness though

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 16:01):

An extra note/tip: In current roc, if you have function args that you feel need names or more distinction, you can always use a record:

when Dict.get graph, current is
    Ok neighbors ->
        # ...
        bfsHelper {isTarget, queue: newQueue, seen: newSeen, graph}
    Err KeyNotFound ->
        bfsHelper {isTarget, rest, seen, graph}

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 16:01):

not saying that fits/is needed here, but can help in some of the more complex cases.

view this post on Zulip Brian Teague (Jan 25 2024 at 16:42):

Is there a precedence issue with this syntax? <function><space><expr>(,<expr>)*

Using a real example, ROC requires parenthesis around toStr to avoid getting TOO MANY ARGS error
Str.concat (Num.toStr 3) "more data"

Also states, "Are there any missing commas? Or missing parentheses?"

Using commas, I don't think parenthesis would be required here, because there is another space after Num.toStr signifying it's a function call with higher precedence, but ROC would have to know Num.toStr only has one argument
Str.concat Num.toStr 3, "more data"

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 16:51):

I guess it is more of a perceived issue than a true issue. As in, to an end user, commas feel like they separate args in a way that parens shouldn't be required. Putting parens in commas feels unnecessary and looks off.

In this case, with commas, you have to write:
Str.concat (Num.toStr 3), "more data"

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 17:02):

In general, without some sort of grouping structure, I think commas become hard to parse as a human.

For lambdas and types they have a grouping structure : -> and \ ->. On top of that, they don't allow for arbitrary expressions. You will never see that syntax nested in itself. With function calls, it can nest.

view this post on Zulip Brian Teague (Jan 25 2024 at 17:34):

Hmm, I think I actually like Str.concat (Num.toStr 3), "more data" with a comma. Also, the lambda for filtered didn't need the extra parenthesis if a comma is present. Also, I don't think ! would need parenthesis either for Set.contains.
filtered = List.keepIf neighbors (\n -> !(Set.contains seen, n))
vs
filtered = List.keepIf neighbors, \n -> !Set.contains seen, n

view this post on Zulip Richard Feldman (Jan 25 2024 at 17:40):

we should probably make an FAQ entry for this :big_smile:

it's been discussed before several times, e.g. https://roc.zulipchat.com/#narrow/stream/231634-beginners/topic/syntax.3A.20whitespace.20vs.20comma.20for.20items

view this post on Zulip Brian Teague (Jan 25 2024 at 17:49):

Like you said @Brendan Hansknecht , it would probably need tuple grouping syntax just like a record {}, (basically a nameless record?)
Str.concat (Num.toStr 3, "more data")
filtered = List.keepIf (neighbors, \n -> !Set.contains (seen, n))
bfsHelper (isTarget, rest, seen, graph)

This would basically require all arguments to either be a list [], record {}, or tuple ()

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 18:02):

Tuples already exist

view this post on Zulip Brian Teague (Jan 25 2024 at 18:05):

And we have come full circle :joy:
If a function could only accept at most one argument: primitive value, record, tuple, or list. I'm curious if that would simplify the parser and the evaluation of the AST.

view this post on Zulip Brian Teague (Jan 25 2024 at 18:41):

Example if ROC used at most one argument like Haskell

when Dict.get (graph, current) is
    Ok neighbors ->
        # filter out all seen neighbors
        filtered = List.keepIf (neighbors ,\n -> !Set.contains (seen, n))

        # newly explored nodes are added to the FIFO queue
        newQueue = List.concat (rest, filtered)

        # the new nodes are also added to the seen set
        newSeen = List.walk (filtered, seen, Set.insert)

        bfsHelper (isTarget, newQueue, newSeen, graph)

    Err KeyNotFound ->
        bfsHelper (isTarget, rest, seen, graph)

view this post on Zulip Brian Teague (Jan 25 2024 at 18:43):

I think function currying and lazy evaluation might be used more as well.

view this post on Zulip Brendan Hansknecht (Jan 25 2024 at 18:44):

I think function currying and lazy evaluation

Roc only has those features if you do them manually

view this post on Zulip Richard Feldman (Jan 25 2024 at 20:00):

Brian Teague said:

If a function could only accept at most one argument: primitive value, record, tuple, or list. I'm curious if that would simplify the parser and the evaluation of the AST.

we've also talked about this! See #ideas > tuples for function arguments


Last updated: Jun 16 2026 at 16:19 UTC