The three major ways to communicate with the outside world in the Elm
Architecture are Cmd a
, Sub a
, and
Html a
. This is a curious thing to say, because none of those
types do anything, in the imperative sense. Regardless, it’s tempting to think
of Http.send
as a function that kicks off an XmlHttpRequest()
in the browser
and you get the result as what you defined as your Msg
type.
It’s also tempting to think of that Time.every 60000
subscription as starting
a setTimeout
. Then you just wait for the every-minute callbacks to roll in.
This is convenient to think about, but it’s wrong. It’s not very wrong, but
it’s wrong in some important ways that can hamper understanding. So, yes, the
end result of the Http.send
is that a request is fired off to the API you want
to hit. And the end result of Time.every
is that you get a callback every so
often. The difference, though, is that it’s not “your code” doing it. It’s the
Elm Runtime.
I’m going to focus on Cmd a
here because that’s the most straightforward
case, but both Sub a
and Html a
deal with things in a similar manner.
Msg In a Bottle
The first important thing to note is that little a
. This makes Cmd a
a
parameterized type the same way that a List a
is a parameterized type. And
just like a List String
describes a List
that deals with String
s, a Cmd
String
describes a Cmd
that deals with a String
. The Cmd
part tells Elm
“This value represents a thing I want you to do” and the String
tells Elm “and
when it’s done, use this value to tell me about it.”
You might see (and probably will see) people using Cmd Msg
fairly often. Msg
isn’t magical, it’s a union type that anyone can define for
themselves. Each tag in the union is a kind of response that
you’re looking for. The name “Msg” is just a convention as to what Elm
programmers should call this data type.
Imagine you have a personal assistant for a second. You give them a note that
says “Buy 2 apples and give them to me.” and they do. It’s their job, after all.
And they come back and say “I got you the apples you asked for”. The assistant
in this scenario is a metaphor for the Elm Runtime, which actually does the
things your Cmd a
s specify. The important thing to know is that you are not
doing the action. You’re telling the Elm Runtime to do the action (“Buy 2
apples”) and to let you know how you want to be told when it’s done (“Give me
the apples”).
OK, so that’s great. I can tell the Elm Runtime to do something for me, but I have a whole application here. I’m a busy developer. Stuff needs to get done! I have to do more than one thing at a time!
Cmd a
is the equivalent of handing your assistant a note with instructions on
it. Fortunately, we can do more. With Cmd.batch
you can combine multiple
commands into one. How do you tell someone to buy multiple things? Same way: you
batch all the things you want up into one shopping list and hand them the list.
Voila! You cam have you assistant buy Apples, buy Cereal, make a request to an
API, and buy Potatoes all in the same trip.
And all this hangs together because of the way that Elm treats effects as data. In an imperative language (like Ruby or JavaScript), when you call a function, something gets done. In Elm, when you call a function a piece of data is produced. The Elm Runtime knows that these pieces of data contain instructions for something you want done, so it does them. This gives us a way to reason about and with those effects (like organizing them to run most efficiently). It also gives us a conceptual purity, because we can treat all these bits of data like any other bits of data. They’re not magical – unless you want to call “a program knows to do something with it” magical, in which case all programming is kinda magical.
Tell Me When It Closes
The conceptual model that we’re talking about here is that you’re telling the Elm Runtime to handle something on your behalf and then waiting for it to be done. And then, because you told Elm how to tell you, it sends you the message you were expecting.
Like the wonderful Tell Me When It Closes, you offload the stress of having to worry about you actions to someone else.
It took me some time to really grok what was going on with the pattern of types
that deal with Msg
and how it fits into the architecture and my thinking. But
once it clicked for me, the whole way of “doing things” and the way it doesn’t
really involve doing things yourself finally came into focus. Sub a
, Html a
,
and ports all operate on more or less this same style of getting things done.