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 that never ends. Note that this task does not return a 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. Check out this example.

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 a the following task;

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

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

result <- canFail |> Task.attempt
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.

For example we can use this to run tasks in sequence like follows;

# Prints "Hello World!\n" to standard output.
{} <- Stdout.write "Hello "|> Task.await
{} <- Stdout.write "World!\n"|> Task.await

Task.ok {}

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,
}

seq : 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.seq 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 -> "..."