In response to this old topic, what do you all think about starting the backpassing explanation in the tutorial with a List.map (instead of await) example.
It would be strange to write real code like that but I think it noticeably improves cognitive load.
After the List.map example we can again talk about the await snippet above the backpassing section but now we have a learning curve that is less steep.
:thinking: is there something other than List.map we could use? It's a reasonable idea, I just don't love starting with a weird example :sweat_smile:
Would it be more understandable if we used result directly instead of task? Technically they are the same API.
But result and try may be more familiar
I think Result is definitely better than Task, but for those not used with functional programming, List.map is a lot easier to get I think.
I'm also definitely still open to for better alternatives to List.map
Yes List.map is the easiest to understand, but it's also true that if we put something in the tutorial, it will be seen as an example of idiomatic normal code. And this isn't.
I think it's easy to understand wanting to chain a sequence of operations that could fail. And Result.try is good for that.
I think Result.try is good in that it's a good fit for backpassing style, but I'm not sure if we want to introduce Result.try at that point in the tutorial to someone who's potentially new to FP :sweat_smile:
I don't think using an unnatural example is necessarily a bad thing, as long as it's overtly unrealistic, something that immediately and only makes sense as a demonstration of the underlying mechanic, even for a naive learner. I think I'd like to see a concise idiomatic use of backpassing, then explain it with a contrived but ultra-clear example, and finally a realistic example.
I do think the explanation could be better, it took me a couple of reads to really get it. I think being explicit about why you would want to use it and using async as the example is the best part of the current explanation. It following the async await part of the tutorial makes the flow in really good. Like at the end of that bit my main thought is "wow the async await syntax is going to be callback hell" and then "oh i guess this is trying to fix that" I don't think i would have had any hope of getting it if it wasn't for that.
I think the key thing is indicating in the code snippet that there is now this new context, that you are calling a new anonymous function each time. This may be best done visually.
Here are two examples of what i mean
image.png
image.png
Overall i think different people will understand it best differently, but one thing that would help everyone is a larger collection of examples. I think just having a little "quick fire section" that shows it being used with async await, results and the list.map example (acknowledging that the last one is a bit silly ofcourse) would really help, that's two that are legit uses within roc, and one that provides a fammilar example that users of other languages instantly understand.
These examples would ideally be followed or preceded by their equivalent versions using anon funcs.
I think by far the worst thing about the current explanation is that it doesn't include an example that shows nested callbacks
result<- await sometask
result2 <- await otherTask
"results \(result) \(result2)"
compared to
await sometask (\result ->
await otherTask (\result2 ->
"results \(result) \(result2)"
)
)
Thereby actually showing why the syntax is useful
The visualizations are a good idea!
A nested example is shown somewhat above the backpassing section.
Last updated: Jun 16 2026 at 16:19 UTC