Task

Task ok err

A Task represents an effect; an interaction with state outside your Roc program, such as the terminal's standard output, or a file.

forever : Task a err -> Task * err

Run a task repeatedly, until it fails with err. Note that this task does not return a success value.

loop : state, (state -> Task [ Step state, Done done ] err) -> Task done err

Run a task repeatedly, until it fails with err or completes with done.

sum =
    Task.loop! 0 \total ->
        numResult =
            Stdin.line
                |> Task.result!
                |> Result.try Str.toU64

        when numResult is
            Ok num -> Task.ok (Step (total + num))
            Err (StdinErr EndOfFile) -> Task.ok (Done total)
            Err InvalidNumStr -> Task.err NonNumberGiven

ok : a -> Task a *

Create a task that always succeeds with the value provided.

# Always succeeds with "Louis"
getName : Task.Task Str *
getName = Task.ok "Louis"

err : a -> Task * a

Create a task that always fails with the error provided.

# Always fails with the tag `CustomError Str`
customError : Str -> Task.Task {} [CustomError Str]
customError = \err -> Task.err (CustomError err)

attempt : Task a b, (Result a b -> Task c d) -> Task c d

Transform a given Task with a function that handles the success or error case and returns another task based on that. This is useful for chaining tasks together or performing error handling and recovery.

Consider the following task:

canFail : Task {} [Failure, AnotherFail, YetAnotherFail]

We can use attempt to handle the failure cases using the following:

Task.attempt canFail \result ->
    when result is
        Ok Success -> Stdout.line "Success!"
        Err Failure -> Stdout.line "Oops, failed!"
        Err AnotherFail -> Stdout.line "Ooooops, another failure!"
        Err YetAnotherFail -> Stdout.line "Really big oooooops, yet again!"

Here we know that the canFail task may fail, and so we use Task.attempt to convert the task to a Result and then use pattern matching to handle the success and possible failure cases.

await : Task a b, (a -> Task c b) -> Task c b

Take the success value from a given Task and use that to generate a new Task.

We can await Task results with callbacks:

Task.await (Stdin.line "What's your name?") \name ->
    Stdout.line "Your name is: $(name)"

Or we can more succinctly use the ! bang operator, which desugars to await:

name = Stdin.line! "What's your name?"
Stdout.line "Your name is: $(name)"

onErr : Task a b, (b -> Task a c) -> Task a c

Take the error value from a given Task and use that to generate a new Task.

# Prints "Something went wrong!" to standard error if `canFail` fails.
canFail
|> Task.onErr \_ -> Stderr.line "Something went wrong!"

map : Task a c, (a -> b) -> Task b c

Transform the success value of a given Task with a given function.

# Succeeds with a value of "Bonjour Louis!"
Task.ok "Louis"
|> Task.map (\name -> "Bonjour $(name)!")

mapErr : Task c a, (a -> b) -> Task c b

Transform the error value of a given Task with a given function.

# Ignore the fail value, and map it to the tag `CustomError`
canFail
|> Task.mapErr \_ -> CustomError

fromResult : Result a b -> Task a b

Use a Result among other Tasks by converting it into a Task.

batch : Task a c -> Task (a -> b) c -> Task b c

Apply a task to another task applicatively. This can be used with ok to build a Task that returns a record.

The following example returns a Record with two fields, apples and oranges, each of which is a List Str. If it fails it returns the tag NoFruitAvailable.

getFruitBasket : Task { apples : List Str, oranges : List Str } [NoFruitAvailable]
getFruitBasket = Task.ok {
    apples: <- getFruit Apples |> Task.batch,
    oranges: <- getFruit Oranges |> Task.batch,
}

sequence : List (Task ok err) -> Task (List ok) err

Apply each task in a list sequentially, and return a list of the resulting values. Each task will be awaited before beginning the next task.

fetchAuthorTasks : List (Task Author [DbError])

getAuthors : Task (List Author) [DbError]
getAuthors = Task.sequence fetchAuthorTasks

forEach : List a, (a -> Task {} b) -> Task {} b

Apply a task repeatedly for each item in a list

authors : List Author
saveAuthor : Author -> Task {} [DbError]

saveAuthors : Task (List Author) [DbError]
saveAuthors = Task.forEach authors saveAuthor

result : Task ok err -> Task (Result ok err) *

Transform a task that can either succeed with ok, or fail with err, into a task that succeeds with Result ok err.

This is useful when chaining tasks using the ! suffix. For example:

# Path.roc
checkFile : Str -> Task [Good, Bad] [IOError]

# main.roc
when checkFile "/usr/local/bin/roc" |> Task.result! is
    Ok Good -> "..."
    Ok Bad -> "..."
    Err IOError -> "..."