Stream: beginners

Topic: Funnel task errors to single error type?


view this post on Zulip Noel R (Mar 30 2024 at 19:27):

As a learning exercise, I'm trying to write a function that attempts to read a file that may not exist, and then:

Real use case:

My haphazard attempts to get this to work have failed. Here's the latest failed attempt:

main =
    outputFileMapErr
    |> Task.onErr handleErr

outputFileMapErr =
    # A file that does not exist
    path = Path.fromStr "someFile"

    # This could fail if the file doesn't exist or is unreadable
    result <- File.readUtf8 path
    |> Task.attempt
    |> Task.mapErr \err -> Task.err MyCustomErr "Some error message."

handleErr =
    msg = when err is
        MyCustomErr msg -> msg
    Stdout.line "We stumbled upon this: $(msg)"

What could I do with the results of File.readUtf8 to get the desired behavior?

view this post on Zulip Jasper Woudenberg (Mar 30 2024 at 21:31):

Hi! I've played around with that code a bit and made a couple of tweaks to get to this modified version:

main =
    outputFileMapErr
    |> Task.onErr handleErr

outputFileMapErr =
    # A file that does not exist
    path = Path.fromStr "someFile"

    # This could fail if the file doesn't exist or is unreadable
    result <-
        File.readUtf8 path
        |> Task.mapErr \err -> MyCustomErr "Some error message."
        |> Task.await

    Stdout.line result

handleErr = \err ->
    when err is
        MyCustomErr msg ->
            Stdout.line "We stumbled upon this: $(msg)"

Two small fixes:

Let me know if anything is unclear, or if I misunderstood what you were trying to do!

view this post on Zulip Noel R (Mar 31 2024 at 03:22):

Thank you Jasper! That was very helpful.

For anyone trying to understand backpassing and the pipe operator, this is what the function above looks like without them:

outputFileMapErr =
    # A file that does not exist
    path = Path.fromStr "someFile"

    # Task.mapErr takes two arguments:
    # - A task
    # - A function that takes an error and outputs another error
    Task.mapErr
        # First arg should be a Task, which is what calling Task.await produces
        # NOTE that we must wrap the call to Task.await in parentheses
        # so we pass the resulting Task to mapErr, and noth the individual
        # parts (Task.await itself, the result from calling readUtf8, the anonymous function)
        (Task.await

            # Task await _also_ takes a task as first arg, which is what
            # File.readutf8 produces. Again, we wrap in parentheses to
            # pass the output and not the function and _path_ individually
            (File.readUtf8 path)

            # The second arg to await is a function that _also_ returns
            # a task.
            \result -> Stdout.line "What we got is: $(result)")

        # The second arg to mapErr is a function that takes an error and
        # returns a different error.
        # Note that the MyCustomErr tag is nothing special: we could have
        # used any tag name, as long as it follows the tag naming conventions.
        \_ -> MyCustomErr "Some error msg."

Relevant docs:

view this post on Zulip Notification Bot (Mar 31 2024 at 03:25):

Noel R has marked this topic as resolved.

view this post on Zulip Brendan Hansknecht (Mar 31 2024 at 06:04):

@Noel R , your desugaring is not quite right. That said, I think your reordering is also correct in this specific case.

This is an exact desugaring.

outputFileMapErr =
    # A file that does not exist
    path = Path.fromStr "someFile"

    # This could fail if the file doesn't exist or is unreadable
    Task.await (
        Task.mapErr (File.readUtf8 path) \err -> MyCustomErr "Some error message."
        \result -> Stdout.line result

view this post on Zulip Brendan Hansknecht (Mar 31 2024 at 06:06):

The nesting of Task.await and Task.mapErr was flipped.

view this post on Zulip Notification Bot (Mar 31 2024 at 18:39):

Noel R has marked this topic as unresolved.

view this post on Zulip Noel R (Mar 31 2024 at 18:44):

Thank you Brendan for pointing that out. This sugar sprinkling / desugaring is a good learning exercise.


Last updated: Jul 26 2025 at 12:14 UTC