Stream: beginners

Topic: Does Task.onErr re-map errors the way I'm trying to use it?


view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:19):

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, …]

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:21):

(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:)

view this post on Zulip Luke Boswell (Dec 16 2023 at 04:22):

Can you share your full code please? This seems a bit strange

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:22):

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

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:23):

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:

view this post on Zulip Brendan Hansknecht (Dec 16 2023 at 04:26):

Calling the wrong function inrun? contents <- File.readUtf8 inputPath |> await

view this post on Zulip Brendan Hansknecht (Dec 16 2023 at 04:26):

Should be contents <- readFile inputPath |> await

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:26):

:face_palm:

view this post on Zulip Luke Boswell (Dec 16 2023 at 04:27):

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

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:27):

Ah, that's cleaner.

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:27):

Thanks!

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:28):

lol, and I thought adventofcode day 14 might be fun to try to use for learning Roc :joy:

view this post on Zulip Luke Boswell (Dec 16 2023 at 04:28):

Yeah, Tasks can be tricky to start with.

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:28):

I guess I picked the right day… Sisyphus and all that…

view this post on Zulip Luke Boswell (Dec 16 2023 at 04:29):

It took me a while to grok them. But they're pretty awesome, so worth the time to learn.

view this post on Zulip Luke Boswell (Dec 16 2023 at 04:31):

Without the trailing _ the errors wont merge very well into the single handleErr

view this post on Zulip Zellyn Hunter (Dec 16 2023 at 04:37):

hmm not sure I understand that last part…

view this post on Zulip Luke Boswell (Dec 16 2023 at 05:02):

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.

view this post on Zulip Zellyn Hunter (Dec 17 2023 at 01:54):

Ah, thanks, that's helpful.


Last updated: Jul 06 2025 at 12:14 UTC