What is the 'msg' in 'HTML msg' in Elm actually?
Asked Answered
B

2

15

I'm teaching myself elm and see (of course) many references to Html msg--

I understand that this is a 'parameterised type', that is, that (as I understand it), the constructor of the type Html takes a parameter -- much as List Char does.

OK. But then in following along in some tutorials, I see they quickly change the msg to be a custom type, often something like this (I'm doing this from memory, so please forgive me):

Html Msg

where Msg might be defined as

type Msg =
  Something | SomethingElse

I also see that here -- https://discourse.elm-lang.org/t/html-msg-vs-html-msg/2758 -- they say

The lower case msg is called a type variable. The upper case Msg is called a concrete type - its something your application has defined.

That answers my question part way, but could someone elaborate on what this means exactly? Because when I see something like List String, I understand what String is in this context, but I don't understand what msg means in Html msg.

Also, are we not changing the type of the return value of the function? That is, if the Elm runtime is expecting view to return a certain type, and that type is Html msg, if we change the return type to Html Whatever, why does that work? (For instance, we can't change a function's return value from List String to List Number arbitrarily, right?)

Coming from a background in OOP and typed languages like C, TypeScript, etc, I would think that any Msg would need to somehow be related to msg, that is 'extend' it in some way to allow polymorphism. Obviously I'm looking at this the wrong way, and any explanation would be appreciated!

Braid answered 23/11, 2019 at 13:5 Comment(1)
I think this has been answered really well here stackoverflow.com/a/46038459Sirmons
P
16

tl;dr: as long as every function agrees on the type of msg, and model, they can be anything you want.

Just like the type parameter of List, msg can be anything. There is no restriction. To demonstrate, here's the classic increment/decrement example with the msg being just an Int:

-- These type aliases aren't needed, but makes it easier to distinguish
-- their roles in later type signatures
type alias Model = Int
type alias Msg = Int


update : Msg -> Model -> Model
update msg model =
    model + msg


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick 1 ] [ text "+1" ]
        , div [] [ text <| String.fromInt model.count ]
        , button [ onClick (-1) ] [ text "-1" ]
        ]


main : Program () Model Msg
main =
    Browser.sandbox { init = 0, view = view, update = update }

So how does that work? The key is in the Browser.sandbox function, as that's what connects everything together. Its type signature is:

sandbox :
    { init : model
    , view : model -> Html msg
    , update : msg -> model -> model
    }
    -> Program () model msg

where model and msg are type variables. Both of these can be replaced with any concrete type, as long as it matches up with the constraints given in this signature, which is that they must be the same across the init, view and update functions. Otherwise we'll get a type error here. If, for example, update requires a String instead of an Int, we'll get the error:

This argument is a record of type:

    { init : Model, update : String -> Model -> Model, view : Model -> Html Msg
    }

But `sandbox` needs the 1st argument to be:

    { init : Model
    , update : String -> Model -> Model
    , view : Model -> Html String
    }

It doesn't really know which is right or wrong, of course, just that there's a mismatch. But in an effort to be helpful it assumes String is right and expects view to return Html String instead.

I hope that sufficiently answers your question. If not, comment and subscribe!

Phenobarbitone answered 23/11, 2019 at 13:27 Comment(2)
Does one then always need to replace msg with your own type? That is, one could not just leave it Html msg, but instead tell the system exactly what kind of message your going to be sending in view and receiving in update?Braid
You don't. You can write functions that deal wth any kind of msg. That's what Browser.sandbox does, after all. It just passes the values around, like a box that can hold anything. You're also not required to use a concrete type with view and update, but of course that isn't going to be a very useful application. It will be completely static.Phenobarbitone
M
3

Short and Simple Explanation:

I have an xmas function like this:

wrapPresents : presents -> String

But what is the type, presents? It can be anything that you want! You can substitute it with your own 'concrete type'. presents is just a place holder!

wrapPresents : Boxes -> String

Now we have the "type" - we know that the first parameter must be a Box type.

wrapPresents boxes = "All the boxes are now wrapped!"

Summary: msg (lowercase) is simply a placeholder

Maxson answered 26/11, 2019 at 21:22 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.