Stream: beginners

Topic: Result.map_both example compilation error


view this post on Zulip Alex Kelley (Mar 02 2025 at 19:09):

I'm having trouble compiling the following expression. The compiler (latest) gets stuck on ', Stdout. line...', expecting a close paren. I tried looking into the GitHub repository to see where these functions are declared and what tests are being run, but I'm coming up short. Does anyone see a problem with my expression below?

    joined_letters =
        Env.decode!("LETTERS")
        |> Result.map_both(|letters, err| Str.join_with(letters, " "), Stdout.line!(${List.take_first(err, 0)}))
        |> try

    Stdout.line!("Your favorite letters are: ${joined_letters}")?

Thanks!

view this post on Zulip hchac (Mar 02 2025 at 19:52):

Hi Alex,

I think the current issue you're facing is the Stdout.line!(${List.take_first(err, 0)}) in the map_both needs to have its argument be a string: Stdout.line!("${List.take_first(err, 0)}") (just inserted double quotes)

view this post on Zulip hchac (Mar 02 2025 at 20:14):

In terms of the program itself: are you doing a List.take_first(err, 0) because of the return type of decode!?

decode! :
    Str
    => Result val
        [
            VarNotFound,
            DecodeErr DecodeError
        ]
    where val implements Decoding

Here the Err of Result is [VarNotFound, DecodeErr DecodeError], though this looks like a List, it's not actually one that you access with List.<somefunc>. This is a "list" of Tags, but only one of these Tags is actually returned in the Result. This [...] syntax in the Result type is just saying that any one of these is possible to be returned (but only 1 of them will be) as the Err.

I've changed that part of the code to the following, which is what I believe you were trying to do:

main! = |_args|
    joined_letters =
        Env.decode!("LETTERS")
        |> Result.map_both(
            |letters|
                Str.join_with(letters, " "),
            |err|
                err,
        )
        |> try

    Stdout.line!("Your favorite letters are: ${joined_letters}")

Note that map_both takes as its second and third arguments two separate functions, one to handle the Ok and another to handle the Err.

In this scenario, you're really just modifying the Ok path with the Str.join_with, so you can swap out map_both to instead be map_ok:

    joined_letters =
        Env.decode!("LETTERS")
        |> Result.map_ok(
            |letters|
                Str.join_with(letters, " "),
        )
        |> try

And one final thing you can do, is swap out the |> try at the end for a ?:

    joined_letters =
        Env.decode!("LETTERS")
        |> Result.map_ok(|letters| Str.join_with(letters, " "))?

view this post on Zulip Alex Kelley (Mar 02 2025 at 22:31):

@hchac Thank you very much for your explanation. You are correct. I mistakenly thought Env. decode! was returning a list of errors. With that misconception cleared up, the code runs as expected. You also reminded me that ? can be substituted for try. Thanks for that, as well.

Wow! What a great community! Thanks again for taking the time to respond to my question.


Last updated: Jul 05 2025 at 12:14 UTC