Skip to content

Instantly share code, notes, and snippets.

@greim
Last active March 5, 2018 18:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greim/75c1c4a7c39b5b8d37edffe7f0aa8660 to your computer and use it in GitHub Desktop.
Save greim/75c1c4a7c39b5b8d37edffe7f0aa8660 to your computer and use it in GitHub Desktop.

Elm: Supplementary Notes

In which I get angry because some aspect of Elm seems weird, and the docs aren't helping, so I jot down these notes, because writing forces me to think deeply about things in a way that I'm incapable of doing otherwise gaaasspp

Grokking Markup

Building markup in Elm entails building a data structure, much like React's virtual DOM. That is, you're not building DOM nodes directly, but rather a lightweight data structure that tells the Elm runtime which DOM nodes you want to exist and it figures it out from there. There's really nothing special about Elm's Html; it's just another data type.

What does Html msg actually mean?

Html is a parameterized type in Elm. Another such is List. Thus, you'll be referred to List a and Html msg in the docs. The parameters, a and msg, stand in for concrete types which you declare in your program, such as List Coord and Html Msg.

It's tempting to read List Coord in English as a list which contains coordinates. This isn't wrong, but it can be misleading. A technically correct description is to replace "which contains" with "associated with", as in, a list associated with coordinates.

That's because a type might not actually contain the things it's associated with. The association can mean that, as in the case of List, but it might mean something else entirely, as is the case of Html.

Why belabor the distinction? If we supposed instances of Html somehow contained instances of messages, that's confusing since it would violate time. HTML can't know in advance what messages it will send. Rather, HTML produces messages, which makes more sense because it doesn't violate time. List Coord is a contains association, and Html Msg is a produces association. The meaning of the association is fleshed out in the actual implementation.

So can there be Html Int then?

Given Html is a parameterized type, is it possible to have HTML produce integers, instead of messages? Yes, but it wouldn't be as useful. Let's examine why by seeing what would happen if we hack the Elm architecture tutorial's "button" example, replacing Msg with Int.

import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)

main =
  Html.beginnerProgram { model = model, view = view, update = update }

-- MODEL

type alias Model = Int

model : Model
model =
  0

-- UPDATE

-- not going to use this
-- type Msg = Increment | Decrement

-- hack our update function to use Ints
-- update : Msg -> Model -> Model
update : Int -> Model -> Model
update n model =
  case n of
    0 ->
      model - 1

    1 ->
      model + 1

    _ ->
      model

-- VIEW

-- hack our view function to create Html Int
-- view : Model -> Html Msg
view : Model -> Html Int
view model =
  div []
    [ button [ onClick 0 ] [ text "-" ]
    , div [] [ text (toString model) ]
    , button [ onClick 1 ] [ text "+" ]
    ]

If you run this, it actually works, but it should be obvious that we've shoe-horned Int into a role it isn't well-suited for, especially considering an Int value can't carry a payload ("tag a value" in Elm-speak) like a union type can.

So how the heck does event handling work?

As you know, HTML elements receive attributes and children. That is, a list of Attribute instances and a list of Html instances.

All attributes are message-producing...

Like Html, Attribute in Elm is a parameterized type, as in Attribute msg. This is mildly confusing, since some attributes don't produces messages. What would it mean for an id attribute to produce a message? That would be crazy. Just comfort yourself in the knowledge that it never will, and continue reading...

...but only event handlers produces messages

An event handler in Elm is just another kind of attribute which has the honorable distinction of being something that actually does produce messages. In that sense it should probably be renamed event producer. But whatever. Thus, when you create an event handler, you're simply creating an attribute for an HTML (or SVG) element, along with instructions for what message it produces.

The Elm package docs for Html.Events.onClick describe it thus:

onClick : msg -> Attribute msg

In English, this means, create an onClick attribute by giving it the message you want sent when the element is clicked.

Apparent time-violation

This worries me, because it seems to violate time. In other words, I'm forced to know at render time which message I want sent at click time, but render happens before click. Ack!

That in turn reveals things about my expectations. I expect there will be information available at click time that isn't available at render time, which would influence my decision about what message to send. Is this a valid assumption? No and yes.

No

No, because it assumes the state of the world would be different at click time than at render time. Elm is an immutable language with pure functions and one-way data-flow, therefore the state of the world and the state of my Elm model are the same, for all I'm concerned about. If and when that state changes, my program re-renders. That's what it means for the application view to be a pure fuction of state.

Now it certainly might be the case that I'm failing to reflect some aspect of world-state in my model. For example, I might want to send message A if the viewport is below a certain width, and B otherwise. But all that means is I need to bring the viewport width into my model, so it can inform the view rendering. This would likely be done using some combination of init and subscriptions.

Yes

The above is a lie! Some aspects of world-state simply aren't accessible to Elm until click time; namely information contained in JavaScript's event objects. What if I want to act conditionally based on event.clientX or event.keyCode for example? It seems my time-violation worries are valid after all!

Fortunately, Elm has a solution for this, by providing custom event handlers which accept JSON decoders instead of messages.

Wait, what... JSON?

Don't worry, this confused me too. What does JSON have to do with events? I wrote a whole other thing to address that confusion, but suffice to say Elm's JSON decoders are neither JSON nor decoders. Rather, they're translators which take arbitrary JavaScript values and produce Elm ones. For example, it's possible to construct a JSON decoder which takes an event object and produces a Msg value.

Crafting such a decoder is beyond the scope of this writing, but suppose we have one called eventToMessage. All we need to do is pass it to Elm's custom event handler on.

button [on "click" eventToMessage] [text "click me"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment