Stream: ideas

Topic: Desugared syntax in error messages


view this post on Zulip David Dunn (Sep 15 2022 at 22:49):

I ran across an error like this:

── TYPE MISMATCH ───────────────────────────── examples/interactive/rocsay.roc ─

This 2nd argument to await has an unexpected type:

24│>                  template <- File.readUtf8 templatePath |> Task.await
25│>
26│>                  roc = template
27│>                      |> Result.try (\t -> Str.replaceEach t "\\t" top)
...
33│>                      |> Result.withDefault "Templating failed!"
34│>
35│>                  Stdout.line "\(roc)"

The argument is an anonymous function of type:

    Result Str [NotFound]* -> Task ...

But await needs its 2nd argument to be:

    Str -> Task ...

I did a double-take at "This 2nd argument to await" before realising that backpassing is similar to bind in do-notation (with await being an explicit bind). So it must desugar to an anonymous function and it makes sense that it's passed to await. It took a moment to jump through these steps though, and I don't think I would've gotten it if I hadn't used bind before. I also had an expectation from other languages that await would take a single Promise/Task arg and produce a value.

I know backpassing is covered in the tutorial and will likely remain a part of learning Roc, but would it be useful to have a line showing the desugared function? Something like this:

── TYPE MISMATCH ───────────────────────────── examples/interactive/rocsay.roc ─

This 2nd argument to await has an unexpected type:

24│>                  template <- File.readUtf8 templatePath |> Task.await
25│>
26│>                  roc = template
...

Backpassing with <- is desugared to a function that's passed as an argument
on the right side of <- like this:

24│>                  File.readUtf8 templatePath |> Task.await (\template ->
25│>
26│>                  roc = template

The argument is an anonymous function of type:

    Result Str [NotFound]* -> Task ...

But await needs its 2nd argument to be:

    Str -> Task ...

Does the desugared form make it clearer how await is taking a second argument? Is it worth adding this to the error message?

view this post on Zulip Ayaz Hafiz (Sep 15 2022 at 23:02):

I think the error message should be improved to say something like "this backpassed function has type ..." instead of the "2nd argument" message it mentions now. I think elaborating the desugared form makes sense in a context where we go into more detail about the error (e.g. in a webpage linked from the error, or something like rustc explain). I'd be a bit hesitant about adding it to all error messages, because it might be a bit noisy once you are familiar with backpacking (and there is a better error message), just my opinion though

view this post on Zulip Richard Feldman (Sep 15 2022 at 23:17):

yeah that seems like a good incremental improvement!

view this post on Zulip Richard Feldman (Sep 15 2022 at 23:18):

I wonder what the with syntax we've discussed could unlock in terms of error message quality in this case :thinking:

view this post on Zulip jan kili (Sep 15 2022 at 23:20):

Great point, @doubledup - no matter how we improve that, we definitely should

view this post on Zulip David Dunn (Sep 16 2022 at 06:42):

A separate webpage is a great idea. I cut out a lot of the error message and it's already quite long. Can definitely see it being noisy when you're already familiar with backpassing.


Last updated: Jun 16 2026 at 16:19 UTC