Stream: ideas

Topic: Removing multi-backpassing?


view this post on Zulip Joshua Warner (Jul 06 2023 at 02:45):

Hi! I've been discussing in various contexts how multi-backpassing is quite painful to parse correctly.

Here's an example of multi-backpassing, in case you're not familiar:

bar = \x, func ->
    func x (x*2)

foo = \x ->
    y, z <- bar x
    y + z

# equivalent to:
foo = \x ->
    bar x (\y, z ->  y + z)

(Note, this is just about multi-backpassing, not single-backpassing or backpassing in general)

Parsing multi-backpassing is difficult because the , can often end up confused for a list, tuple, or record separator if we're parsing anywhere inside one of those - and we end up needing to tip-toe around in the parser to avoid accidentally making the wrong choice. The core problem is _ambiguity_ - and it's not unsolvable, but it does make the parser a lot more challenging to work on.

Anyway - the thing I'd like to propose / get feedback on is _removing_ multi-backpassing, since I've heard it's pretty rare anyway. This would start out with a version of the compiler that emits an error if we detect multi-backpassing and give an appropriate suggestion for fixing (which is always possible, since this is purely syntactic sugar).

Again, this doesn't affect normal "single" backpassing, where you only have a single argument - just y instead of y, z.

Anyone using multi-backpassing on a regular basis? What's your use case? Would you be significantly inconvenienced if that was removed from the language?

view this post on Zulip Luke Boswell (Jul 06 2023 at 03:53):

I didn't know we could do that currently. I kind of have to really think about it to understand what is happening here.

To check I understand correctly, for this example above, if the second argument to bar was a function that took only a single argument then this would be single-backpassing?

view this post on Zulip Joshua Warner (Jul 06 2023 at 03:56):

Yeah, but then obviously the callsite would have to be different. This is an equivalent example with single backpassing:

bar = \x, func ->
    func (x*2)

foo = \x ->
    y <- bar x
    y + 1

# equivalent to:
foo = \x ->
    bar x (\y ->  y + 1)

view this post on Zulip Brendan Hansknecht (Jul 06 2023 at 04:12):

Personally not a fan. I think it is useful and currently we have a limited set of use cases. As platforms expand, I would expect it to become more common. Of course, if we opt for tuples, it should fix the issue, but many functions may not use tuples by default. Personally, I use backpassing essentially everywhere I can and would prefer not to restrict it.

A simple use case is walking over a dictionary. That has 2 values and is great to use backpassing.

view this post on Zulip Brendan Hansknecht (Jul 06 2023 at 04:14):

Similarly, any function with an accumulator and element also have 2 inputs.

view this post on Zulip Brendan Hansknecht (Jul 06 2023 at 04:14):

So the walk type functions (can't always use backpassing with them, but nice when it works out and you can).

view this post on Zulip Joshua Warner (Jul 07 2023 at 01:37):

Interesting; ok.

view this post on Zulip Joshua Warner (Jul 07 2023 at 01:38):

As platforms expand, I would expect it to become more common.

@Brendan Hansknecht can you say more about what use cases you're expecting to grow here?

view this post on Zulip Brendan Hansknecht (Jul 07 2023 at 02:37):

Any task that returns more than one result or in general has a continuation with more than one parameter. Currently we may only have simple functions like Stdin.line which on returns a single string, but the apis easily could expand to more full features functions. Maybe they will always return records of data and that won't be a problem, but this seems like a significant area of API space.

A simple example may be a function that registers a callback for web. It may have a continuation with 3 args, exact url, url params, and body of the request.

Any sort of messaging receiver task may give a key and the message value to the callback.

If a task ever takes a walker style function, it may be generated with a state and value in backpassing (I also have done this personally in roc a number of times when walking lists or dictionaries)


Last updated: Jun 16 2026 at 16:19 UTC