lamdera/program-test/2.0.0

NOTE: the latest version is 4.0.0

A Lamdera package. It is not on package.elm-lang.org: the Lamdera compiler provides it, from static.lamdera.com, so lamdera install works and elm install does not.

Effect.Task

Tasks make it easy to describe asynchronous operations that may fail, like HTTP requests or writing to a database.

Tasks

type alias Task restriction x a =
Task restriction x a

Here are some common tasks:

In each case we have a Task that will resolve successfully with an a value or unsuccessfully with an x value. So Browser.Dom.focus we may fail with an Error if the given ID does not exist. Whereas Time.now never fails so I cannot be more specific than x. No such value will ever exist! Instead it always succeeds with the current POSIX time.

More generally a task is a description of what you need to do. Like a todo list. Or like a grocery list. Or like GitHub issues. So saying "the task is to tell me the current POSIX time" does not complete the task! You need perform tasks or attempt tasks.

(a -> msg)
-> Task restriction Never a
-> Command restriction toMsg msg

Like I was saying in the Task documentation, just having a Task does not mean it is done. We must command Elm to perform the task:

-- elm install elm/time




import Task
import Time


type Msg
    = Click
    | Search String
    | NewTime Time.Posix


getNewTime : Cmd Msg
getNewTime =
    Task.perform NewTime Time.now

If you have worked through guide.elm-lang.org (highly recommended!) you will recognize Cmd from the section on The Elm Architecture. So we have changed a task like "make delicious lasagna" into a command like "Hey Elm, make delicious lasagna and give it to my update function as a Msg value."

(Result x a -> msg)
-> Task restriction x a
-> Command restriction toMsg msg

This is very similar to perform except it can handle failures! So we could attempt to focus on a certain DOM node like this:

-- elm install elm/browser




import Browser.Dom
import Task


type Msg
    = Click
    | Search String
    | Focus (Result Browser.DomError ())


focus : Cmd Msg
focus =
    Task.attempt Focus (Browser.Dom.focus "my-app-search-box")

So the task is "focus on this DOM node" and we are turning it into the command "Hey Elm, attempt to focus on this DOM node and give me a Msg about whether you succeeded or failed."

Note: Definitely work through guide.elm-lang.org to get a feeling for how commands fit into The Elm Architecture.

Chains

(a -> Task restriction x b)
-> Task restriction x a
-> Task restriction x b

Chain together a task and a callback. The first task will run, and if it is successful, you give the result to the callback resulting in another task. This task then gets run. We could use this to make a task that resolves an hour from now:

-- elm install elm/time




import Process
import Time


timeInOneHour : Task x Time.Posix
timeInOneHour =
    Process.sleep (60 * 60 * 1000)
        |> andThen (\_ -> Time.now)

First the process sleeps for an hour and then it tells us what time it is.

succeed : a -> Task restriction x a

A task that succeeds immediately when run. It is usually used with andThen. You can use it like map if you want:

import Time




-- elm install elm/time
timeInMillis : Task x Int
timeInMillis =
    Time.now
        |> andThen (\t -> succeed (Time.posixToMillis t))
fail : x -> Task restriction x a

A task that fails immediately when run. Like with succeed, this can be used with andThen to check on the outcome of another task.

type Error
    = NotFound


notFound : Task Error a
notFound =
    fail NotFound
sequence : List (Task restriction x a) -> Task restriction x (List a)

Start with a list of tasks, and turn them into a single task that returns a list. The tasks will be run in order one-by-one and if any task fails the whole sequence fails.

sequence [ succeed 1, succeed 2 ] == succeed [ 1, 2 ]

Maps

map : (a -> b) -> Task restriction x a -> Task restriction x b

Transform a task. Maybe you want to use elm/time to figure out what time it will be in one hour:

import Task exposing (Task)
import Time




-- elm install elm/time
timeInOneHour : Task x Time.Posix
timeInOneHour =
    Task.map addAnHour Time.now


addAnHour : Time.Posix -> Time.Posix
addAnHour time =
    Time.millisToPosix (Time.posixToMillis time + 60 * 60 * 1000)
(a -> b -> result)
-> Task restriction x a
-> Task restriction x b
-> Task restriction x result

Put the results of two tasks together. For example, if we wanted to know the current month, we could use elm/time to ask:

import Task exposing (Task)
import Time




-- elm install elm/time
getMonth : Task x Int
getMonth =
    Task.map2 Time.toMonth Time.here Time.now

Note: Say we were doing HTTP requests instead. map2 does each task in order, so it would try the first request and only continue after it succeeds. If it fails, the whole thing fails!

(a -> b -> c -> result)
-> Task restriction x a
-> Task restriction x b
-> Task restriction x c
-> Task restriction x result
(a -> b -> c -> d -> result)
-> Task restriction x a
-> Task restriction x b
-> Task restriction x c
-> Task restriction x d
-> Task restriction x result
(a -> b -> c -> d -> e -> result)
-> Task restriction x a
-> Task restriction x b
-> Task restriction x c
-> Task restriction x d
-> Task restriction x e
-> Task restriction x result

Errors

(x -> Task restriction y a)
-> Task restriction x a
-> Task restriction y a

Recover from a failure in a task. If the given task fails, we use the callback to recover.

fail "file not found"
  |> onError (\msg -> succeed 42)
  -- succeed 42


succeed 9
  |> onError (\msg -> succeed 42)
  -- succeed 9
mapError : (x -> y) -> Task restriction x a -> Task restriction y a

Transform the error value. This can be useful if you need a bunch of error types to match up.

type Error
    = Http Http.Error
    | WebGL WebGL.Error


getResources : Task Error Resource
getResources =
    sequence
        [ mapError Http serverTask
        , mapError WebGL textureTask
        ]