Stream: beginners

Topic: Is it considered a good practice to transform task to result


view this post on Zulip Sajjad Naveed (Jun 10 2024 at 12:24):

I am just trying to play around with tasks to understand them better and see how can I can fit them into my pipeline and so far converting them into Result seems like the easiest and most natural way to me.

e.g. consider this

app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }

import pf.Task
import pf.Stdout

main =
    tsk = Task.result! (tskCheck 1)
    rsp = tskHandle tsk
    dbg rsp

    Stdout.line! "Done"

tskHandle = \tsk ->
    when tsk is
        Ok value -> handleGoodCase value
        Err err -> handleErrorCase err

handleGoodCase = \value ->
    v = Num.toStr value
    dbg "Everything is fine $(v)"


    Good value

handleErrorCase = \error ->
    v = Num.toStr error
    dbg "Something went wrong $(v)"

    Bad error

tskCheck = \input ->
    if input == 0 then
        Task.ok input
    else
        Task.err input

To me this seems like a more cleaner approach then mapErr and/or onErr and keeps everything consistent with the rest of the code that might have other Results as well. But I am not sure if encapsulating Task output in a Result is considered good or bad? Or is it just a matter of choice?

view this post on Zulip Anton (Jun 10 2024 at 13:19):

Using Task.result! followed by a when is a common pattern. I would however write this code differently, I'll play around with it a bit.

view this post on Zulip Sajjad Naveed (Jun 10 2024 at 13:20):

Thanks @Anton . Any pointers will be greatly appreciated. I am trying to learn and do things the right (preferred) way. :pray:

view this post on Zulip Anton (Jun 10 2024 at 13:43):

The example is quite artificial so perhaps I left out too much, anyway I'd say this is normal Roc style:

import pf.Task
import pf.Stdout

main =

    when checkInput 1 |> Task.result! is
        Ok checkedValue ->
            Stdout.line! "Received input $(Num.toStr checkedValue)."

        Err badValue ->
            # exit code 1 to indicate failure
            Task.err (Exit 1 "Received bad input $(Num.toStr badValue).")

# mock Task
checkInput = \input ->
    if input == 0 then
        Task.ok input
    else
        Task.err input

view this post on Zulip Sajjad Naveed (Jun 10 2024 at 13:51):

Yeah this is perfect @Anton .

I just wrote small functions because I was playing around with different aspects of Task and Result. But thank you so very much, this makes perfect sense. It's clean, clear, concise and uses Result.

Really appreciate it @Anton :heart:

view this post on Zulip Anton (Jun 10 2024 at 14:25):

Happy to help :)

view this post on Zulip Brendan Hansknecht (Jun 10 2024 at 14:58):

A general note: I find that the longer and more complex your code gets (especially if errors are handled in many different ways), onErr and mapErr become more and more natural.

I think that result is nice, but if used a lot, can lead to broken up code that is much harder to follow.

view this post on Zulip Sajjad Naveed (Jun 10 2024 at 15:08):

Thanks @Brendan Hansknecht , I will keep that in mind. I am also trying to find some open source projects/tools written in roc to review their code.

All of roc code seems pretty natural to me except Task. When working with Task, even small changes break my code. But I am trying to work with Task as much possible in various ways to get a grasp of it. :pray:


Last updated: Jul 06 2025 at 12:14 UTC