Task - asynchronous operation that may fail

type alias Task err ok = Task err ok

say you have a task with the type Task String User. when you perform the task, it will either fail with a String value or succeed with a User value

succeed : a -> Task x a
a task that succeeds immediately

succeed 42    -- results in 42

fail : x -> Task x a
a task that fails immediately

fail "file not found" : Task String a

* * *

just as Functor in Haskell

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

map sqrt (succeed 9) -- succeed 3

just as Applicative in Haskell

map2 : (a -> b -> r) -> Task x a -> Task x b -> Task x r
put the results of two tasks together. if either task fails, the whole thing fails. it also runs in order so the first task will be completely finished before the second task starts

map2 (+) (succeed 9) (succeed 3)     -- succeed 12

map3 , map4 , map5 

just as Monad in Haskell

andThen : (a -> Task x b) -> Task x a -> Task 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

succeed 2 |> andThen (\n -> succeed (n + 2))  -- succeed 4
this is useful for chaining tasks together: say you need to get a user from your servers and then lookup his/her picture once you know the name

sequence : List (Task x a) -> Task 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 ]
this can be useful if you need to make a bunch of HTTP requests one-by-one

* * *

onError : (x -> Task y a) -> Task x a -> Task y a
recover from a failure in a task. if the given task fails, you use the callback to recover

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

succeed 9 |> onError (\_ -> succeed 42)    -- succeed 9

mapError : (x -> y) -> Task x a -> Task y a
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 ]

* * *

perform : (a -> msg) -> (b -> msg) -> Task a b -> Cmd msg

the only way to do things in Elm is to give commands to the Elm runtime. so we describe some complex behavior with a Task and then command the runtime to perform that task

for example, getting the current time looks like this:

import Task
import Time exposing (Time)

type Msg = Click | Clock Time | Err String
type alias Model = { dat: Time, err: String }

update : Msg -> Model -> (Model, Cmd Msg)
update x y =
  case x of
    Click ->  (y , Task.perform Err Clock Time.now)
    Clock t -> ({y | dat = t} , Cmd.none)
    Err _ -> (y , Cmd.none) 

attempt : (Result x a -> msg) -> Task x a -> Cmd msg
command the Elm runtime to attempt a task that might fail