Hi, new here and trying to figure out some error handling.
My advent of code for 2022 day 01:
app "day01"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.6.0/QOQW08n38nHHrVVkJNiPIjzjvbR3iMjXeFY5w1aT46w.tar.br" }
imports [pf.Stdout, pf.Stderr, pf.Task, pf.File, pf.Path]
provides [main] to pf
inputPath = "day01.txt"
main =
input <- File.readUtf8 (Path.fromStr "input/\(inputPath)") |> Task.attempt
when input is
Ok in -> when part01 in is
Ok out -> Num.toStr out |> Stdout.line
Err e -> when e is
InvalidNumStr -> Stderr.line "Error running part01 for \(inputPath), invalid num str"
ListWasEmpty -> Stderr.line "Error running part01 for \(inputPath), ListWasEmpty"
Err _e -> Stderr.line "Error running part01 for \(inputPath), no file"
part01 : Str -> Result U64 [InvalidNumStr, ListWasEmpty]
part01 = \input ->
parsed <- Str.trim input
|> Str.split "\n\n"
|> List.mapTry (\xs -> Str.split xs "\n" |> List.mapTry Str.toU64)
|> Result.try
List.map parsed List.sum |> List.max
Questions:
mapTry
and Result.try
, are these the best way to "bubble up" errors?main
fn so that I can have just one pattern match for all errors? There's probably something straightforward, but I ran out of steam after figuring out the try
stuff.Task
and Result
.Check out https://www.roc-lang.org/examples/Tasks/README.html. It's only just been updated to hopefully assist with this. Would love to know if it helps you.
Maybe we should rename this now to "Tasks and Error Handling" now it has a slightly bigger emphasis on errors and might make it easier to find?
We could also maybe link to this example in the Tutorial, at the end of the tasks section too.
Thanks for the link. I'd actually skimmed the PR before, but took a closer look now.
It looks like Task.await
is one of the things I'm looking for. It has the same feeling of "bubbling up" as Result.try
. Instead of using Task.attempt
and pattern matching immediately.
One of the things I was missing, and which I don't think is in the Tasks example, is Task.fromResult
. I'm guessing this should be pretty commonly used? Or perhaps there's a better way to compose Result
and Task
. I updated my code, lmk if there's still something to be improved.
main =
run |> Task.onErr handleErr
run : Task.Task {} [FileReadErr, InvalidNumStr, ListWasEmpty]
run =
input <- File.readUtf8 (Path.fromStr "input/\(inputPath)")
|> Task.mapErr \_ -> FileReadErr
|> Task.await
outPart01 <- part01 input |> Task.fromResult |> Task.await
Num.toStr outPart01 |> Stdout.line
handleErr = \err ->
when err is
FileReadErr -> Stderr.line "Error running part01 for \(inputPath), file read err"
InvalidNumStr -> Stderr.line "Error running part01 for \(inputPath), invalid num str"
ListWasEmpty -> Stderr.line "Error running part01 for \(inputPath), ListWasEmpty"
I had a play with your code to explore the Task.fromResult
a bit more. I think I would do it like this (not that this is the correct way or anything, just how I like it).
app "hello-world"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.6.2/c7T4Hp8bAdWz3r9ZrhboBzibCjJag8d0IP_ljb42yVc.tar.br" }
imports [
pf.Stdout,
pf.Stderr,
pf.File,
pf.Path,
pf.Task.{ Task },
]
provides [main] to pf
inputPath = "day01.txt"
run : Task {} [FileReadErr, InvalidNumStr, ListWasEmpty]
run =
outPart01 <-
Path.fromStr "input/\(inputPath)"
|> File.readUtf8
|> Task.mapErr \_ -> FileReadErr
|> Task.await \input -> input |> part01 |> Task.fromResult
|> Task.await
Stdout.line "\(Num.toStr outPart01)"
part01 : Str -> Result U64 [InvalidNumStr, ListWasEmpty]
part01 = \input ->
parsed <-
Str.trim input
|> Str.split "\n\n"
|> List.mapTry (\xs -> Str.split xs "\n" |> List.mapTry Str.toU64)
|> Result.try
List.map parsed List.sum |> List.max
main = run |> Task.onErr handleErr
handleErr = \err ->
when err is
FileReadErr -> Stderr.line "Error running part01 for \(inputPath), file read err"
InvalidNumStr -> Stderr.line "Error running part01 for \(inputPath), invalid num str"
ListWasEmpty -> Stderr.line "Error running part01 for \(inputPath), ListWasEmpty"
I haven't used Task.fromResult
much, it's looks quite useful. Thank you
Thanks for your example! It's very helpful to see how others would approach this
Last updated: Jul 05 2025 at 12:14 UTC