I'm trying to read a file, and convert any of the errors I get into a simple FailedToReadFile
like so:
readFile : Path -> Task Str [FailedToReadFile]
readFile = \path ->
File.readUtf8 path
|> Task.onErr \err ->
Task.err FailedToReadFile
However, it looks like the error details of File.readUtf8
are escaping somehow, and I get this error:
This Task.await call produces:
Task {} [
FileReadErr Path.Path InternalFile.ReadErr,
FileReadUtf8Err Path.Path [BadUtf8 Utf8ByteProblem Nat],
…
]
But the type annotation on run says it should be:
Task {} [FailedToReadFile, …]
(Basically, I'm a noob to Roc (or even something else that has Tasks like Elm), and running aground on the classic "I just want to read a file" tutorial example :disappointed:)
Can you share your full code please? This seems a bit strange
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.0/bkGby8jb0tmZYsy2hg1E_B2QrCgcSTxdUlHtETwm5m4.tar.br" }
imports [
pf.Stdout,
pf.File,
pf.Task.{ Task, await },
pf.Path.{ Path },
pf.Stderr,
pf.Arg,
]
provides [main] to pf
main : Task {} *
main =
run
|> Task.onErr handleErr
Error : [
FailedToReadArgs,
FailedToReadFile,
]
handleErr : Error -> Task {} *
handleErr = \err ->
usage = "roc main.roc -- example"
errorMsg = when err is
FailedToReadArgs -> "Failed to read command line arguments, usage: \(usage)"
FailedToReadFile -> "Failed to read file"
Stderr.line "Error: \(errorMsg)"
run : Task {} Error
run =
{ inputPath } <- readArgs |> await
{} <- Stdout.line "Reading file \"\(Path.display inputPath)\"" |> await
contents <- File.readUtf8 inputPath |> await
Stdout.line "Done"
readFile : Path -> Task Str [FailedToReadFile]
readFile = \path ->
File.readUtf8 path
|> Task.onErr \err ->
Task.err FailedToReadFile
readArgs : Task { inputPath: Path } [FailedToReadArgs]_
readArgs =
args <- Arg.list |> Task.attempt
when args is
Ok ([ _, arg ]) ->
Task.ok { inputPath: Path.fromStr arg }
_ ->
Task.err FailedToReadArgs
I even tried putting a mysterious "to fix bug" underscore on the end of the type definition for readFile
(like on readArgs
from the tutorial), but that didn't help :joy:
Calling the wrong function inrun
? contents <- File.readUtf8 inputPath |> await
Should be contents <- readFile inputPath |> await
:face_palm:
Not sure what is going on yet, but using .mapErr instead works ok
readFile : Path -> Task Str [FailedToReadFile]_
readFile = \path ->
File.readUtf8 path |> Task.mapErr \_ -> FailedToReadFile
Ah, that's cleaner.
Thanks!
lol, and I thought adventofcode day 14 might be fun to try to use for learning Roc :joy:
Yeah, Tasks can be tricky to start with.
I guess I picked the right day… Sisyphus and all that…
It took me a while to grok them. But they're pretty awesome, so worth the time to learn.
Without the trailing _
the errors wont merge very well into the single handleErr
hmm not sure I understand that last part…
The tutorial probably explains it better than I can here in the advanced topics section.
My understanding though is that the tag unions (should be) open in the output position by default (but currently there is a bug), which means they can be combined into a larger tag union. Without the _
the type, with the current implementation this means that the tag union is closed i.e. [A,B]
can only have the tags A
and B
in it. This isn't very helpful when we want to define two different functions x : Result _ [Foo]
and y : Result _ [Bar]
because these errors are different closed tag unions and cannot be combined. Whereas if we add the trailing _
they become open and will accept additional tags in a combined union.
Ah, thanks, that's helpful.
Last updated: Jul 06 2025 at 12:14 UTC