I realized once we have Task as a builtin, we can add a function like this:
List.iter : List elem, s, (s, elem -> Task s err) -> Task s err
basically, iterate over the list and run a task at each step. (If you want to continue iterating without running an effect, you can use Task.ok) If any of the tasks returns an error, the whole thing errors.
if we have this, we might not need List.walkUntil because this already gives you a way to early-exit: return Task.ok normally, and then Task.err when you're done. (Although it does leave you with a Task at the end, so maybe not quite the same...)
could also have a List.iterBackwards too, of course
List.iter can already be implemented today in terms of List.walk, but it would be a nice convenience
Nice!
sequence might also be a nice add on this theme (maybe this has already been discussed, not sure):
List.sequence : List (Task a err) -> Task (List a) err
I.e. collect a bunch of tasks and run them one by one, fail if one fails, or else return the list of success values
Also, F# has a similar function also called iter, though it is a bit simpler:
iter : ('T -> unit) -> 'T List -> unit
(in F# one does not have type-level notation for effects, instead the convention is that an action i.e. an operation with effects returns unit. Also one denotes type variables like 'T.) So this functions runs an action for each element in a list, similar to map.
I guess in Roc this might look like:
List.iter : List a, (a -> Task b err) -> Task (List b) err
Maybe? Not sure if this is less useful than your "walking" suggestion above.
yeah that one's kinda like "map but with tasks" or maybe mapTask
Richard Feldman said:
I realized once we have
Taskas a builtin, we can add a function like this:List.iter : List elem, s, (s, elem -> Task s err) -> Task s errbasically, iterate over the list and run a task at each step. (If you want to continue iterating without running an effect, you can use
Task.ok) If any of the tasks returns an error, the whole thing errors.
This one could be called List.run, it is like a List.walk where it is possible to stumble and get an err :joy:
Actually, iter is a good name for the walking version. Because iterating is done by threading state and updating the state. Just mapping an effect over a list is not really iterating.
This actually relates to some of my motivations in the matrix thread about wanting shapes as types - I wouldn't want to have to handle every possible shape incompatibility error in a long expression (imagine if arithmetic worked like that!). This sort of early-exit that only needs a single error handled is much nicer. Unless that was always possible and I'm a clown.
For results has always been possible
For tasks, much more complicated.
Last updated: Jun 16 2026 at 16:19 UTC