Aggregated documentation of Elm Commands and Subscription
From the package Platform.Cmd
Elm has managed effects, meaning that things like HTTP requests or writing to disk are all treated as data in Elm. When this data is given to the Elm runtime system, it can do some “query optimization” before actually performing the effect. Perhaps unexpectedly, this managed effects idea is the heart of why Elm is so nice for testing, reuse, reproducibility, etc.
A command is a way of telling Elm, “Hey, I want you to do this thing!” So if you want to send an HTTP request, you would need to command Elm to do it. Or if you wanted to ask for geolocation, you would need to command Elm to go get it.
Every
Cmd
specifies (1) which effects you need access to and (2) the type of messages that will come back into your application.
From the package Platform.Sub
A subscription is a way of telling Elm, “Hey, let me know if anything interesting happens over there!” So if you want to listen for messages on a web socket, you would tell Elm to create a subscription. If you want to get clock ticks, you would tell Elm to subscribe to that. The cool thing here is that this means Elm manages all the details of subscriptions instead of you. So if a web socket goes down, you do not need to manually reconnect with an exponential backoff strategy, Elm does this all for you behind the scenes!
Every
Sub
specifies (1) which effects you need access to and (2) the type of messages that will come back into your application.
From the documentation
If you squint, commands and subscriptions are pretty similar to Html values. With Html, we never touch the DOM by hand. Instead we represent the desired HTML as data and let the Elm Runtime do some clever stuff to make it render really fast. It is the same with commands and subscriptions. We create data that describes what we want to do, and the Elm Runtime does the dirty work.
One crucial detail here is that commands and subscriptions are data. When you create a command, you do not actually do it. Same with commands in real life. Let’s try it. Eat an entire watermelon in one bite! Did you do it? No! You kept reading before you even thought about buying a tiny watermelon.
Point is, commands and subscriptions are data. You hand them to Elm to actually run them, giving Elm a chance to log all of this information. In the end, effects-as-data means Elm can:
- Have a general purpose time-travel debugger.
- Keep the “same input, same output” guarantee for all Elm functions.
- Avoid setup/teardown phases when testing update logic.
- Cache and batch effects, minimizing HTTP connections or other resources.So without going too crazy on details, pretty much all the nice guarantees and tools you have in Elm come from the choice to treat effects as data! I think this will make more sense as you get deeper into Elm.
From the FAQ
Cmd is just a bag (i.e. multiset) of chunks of data. It is a functor, but it is not applicative or monadic. This means all you can do is apply a function to all the entries in the bag with map and add to the bag with batch.
Example of Signature of functions that returns Cmd
Random.generate : ( a -> msg) -> Generator a -> Cmd msg
Task.perform : ( a -> msg) -> Task Never a -> Cmd msg
Http.send : (Result x a -> msg) -> Request a -> Cmd msg
Task.attempt : (Result x a -> msg) -> Task x a -> Cmd msg
Websocket.send : String -> String -> Cmd msg
Navigation.newUrl : String -> Cmd msgCmd.none : Cmd msg
Cmd.map : ( a -> msg) -> Cmd a -> Cmd msg
Cmd.batch : List (Cmd msg) -> Cmd msg
Example of Signature of functions that returns Sub
Time.every : Time -> (Time -> msg) -> Sub msg
WebSocket.listen : String -> (String -> msg) -> Sub msg
Mouse.move : (Position -> msg) -> Sub msgSub.none : Sub msg
Sub.map : (a -> msg) -> Sub a -> Sub msg
Sub.batch : List (Sub msg) -> Sub msg
Example
1. Random Numbers
To generate random number:
Random.generate OnResult (Random.int 1 6)
The type signature of Random.generate is
(a -> msg) -> Random.Generator a -> Cmd msg
The type signature of Random.int is
Int -> Int -> Random.Generator Int
so a in this case is Int
OnResul
t need to have type signature
Int -> msg
Something like
type Msg = OnResult Int
So the update function would be something like
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
OnResult res ->
( res, Cmd.none )