Stream: ideas

Topic: iterate while running tasks


view this post on Zulip Richard Feldman (Oct 19 2023 at 16:59):

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...)

view this post on Zulip Richard Feldman (Oct 19 2023 at 17:00):

could also have a List.iterBackwards too, of course

view this post on Zulip Richard Feldman (Oct 19 2023 at 17:02):

List.iter can already be implemented today in terms of List.walk, but it would be a nice convenience

view this post on Zulip Johan Lövgren (Oct 19 2023 at 17:48):

Nice!

view this post on Zulip Johan Lövgren (Oct 19 2023 at 17:52):

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

view this post on Zulip Johan Lövgren (Oct 19 2023 at 18:05):

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.

view this post on Zulip Richard Feldman (Oct 19 2023 at 18:34):

yeah that one's kinda like "map but with tasks" or maybe mapTask

view this post on Zulip Johan Lövgren (Oct 19 2023 at 19:00):

Richard Feldman said:

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.

This one could be called List.run, it is like a List.walk where it is possible to stumble and get an err :joy:

view this post on Zulip Johan Lövgren (Oct 20 2023 at 07:54):

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.

view this post on Zulip Declan Joseph Maguire (Oct 20 2023 at 12:27):

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.

view this post on Zulip Brendan Hansknecht (Oct 20 2023 at 14:24):

For results has always been possible

view this post on Zulip Brendan Hansknecht (Oct 20 2023 at 14:25):

For tasks, much more complicated.


Last updated: Jun 16 2026 at 16:19 UTC