Stream: beginners

Topic: Where is error in Roc Tasks


view this post on Zulip Andrey Stegno (Jan 07 2024 at 18:00):

Hey guys. My friend advised me to look into roclang. As far he knows I love live script. I would recommend to look into live script and check some ideas too.

view this post on Zulip Andrey Stegno (Jan 07 2024 at 18:01):

http://livescript.net lots of identical concepts here

view this post on Zulip Andrey Stegno (Jan 07 2024 at 18:06):

but the difference in live script I can make err, data <- func ....

view this post on Zulip Andrey Stegno (Jan 07 2024 at 18:06):

here url <- File.readUtf8 path |> Task.await

view this post on Zulip Andrey Stegno (Jan 07 2024 at 18:06):

where is err?

view this post on Zulip Notification Bot (Jan 07 2024 at 18:20):

5 messages were moved here from #ideas > import + decode? by Brendan Hansknecht.

view this post on Zulip Brendan Hansknecht (Jan 07 2024 at 18:20):

Task.await only maps the happy case.

view this post on Zulip Brendan Hansknecht (Jan 07 2024 at 18:21):

It will just let all of the error cases accumulate together.

view this post on Zulip Brendan Hansknecht (Jan 07 2024 at 18:21):

Task.attempt would instead give a result for handling the error immediately.

view this post on Zulip Karakatiza (Jan 07 2024 at 18:45):

https://www.roc-lang.org/packages/basic-cli/Task#await
Error value type union is the second member of Task type (b in the example Task c b)
In the case of an error there are multiple ways to handle / change the error type:
onErr : Task a b, (b -> Task a c) -> Task a c lets you use a handler function that takes an error type and returns a task, to process the error case with side effects and produce either successful result or an error of different type;
attempt : Task a b, (Result a b -> Task c d) -> Task c lets you process both the happy case and error case in the same function;
With mapErr : Task c a, (a -> b) -> Task c b you simply convert the type of your error value without handling it

Example:

task =
  data <- File.readUtf8 (Path.fromStr "myfile.txt") |> Task.await
  # At this point you have the type Task Str [ReadErr]
  File.write (Path.fromStr "output.json") { some: "json stuff" } Json.toCompactUtf8
  # At this point you have the type Task {} [ReadErr, WriteErr]
task2 = task |> Task.onErr \err -> when err is
  ReadErr -> Stderr.line "Something went wrong!" # We decide to only handle ReadErr
  e -> Task.err e # We return a task that returns all other errors as is
# Type of task2 is Task {} [WriteErr]

In summary, the errors can be immediately handled like in LiveScript, but usually are accumulated until a later error handling

view this post on Zulip Karakatiza (Jan 07 2024 at 18:52):

LiveScript-style, above could be changed to:

data <- File.readUtf8 (Path.fromStr "myfile.txt")
  |> Task.onErr (\e -> readErrToStr e |> Stderr.line)
  |> Task.await
# At this point you have the type Task Str []
{} <- File.write (Path.fromStr "output.json") { some: "json stuff" } Json.toCompactUtf8
  |> Task.onErr (\e -> writeErrToStr e |> Stderr.line)
  |> Task.await
# At this point you have the type Task {} []

view this post on Zulip Karakatiza (Jan 07 2024 at 20:08):

You don't have err, data <- task in Roc because this syntax combines an error case and a happy case in a single code path where both err and data are nullable. This is not considered nice so there are different patterns for error handling.


Last updated: Jul 06 2025 at 12:14 UTC