Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the 'msg' in 'HTML msg' in Elm actually?

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!

like image 491
Cerulean Avatar asked Nov 23 '19 13:11

Cerulean


2 Answers

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!

like image 117
glennsl Avatar answered Sep 27 '22 00:09

glennsl


The answer to your question is best illustrated by example.

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 wrapPresents boxes = "All the boxes are now wrapped!"

Summary: msg (lowercase) is simply a placeholder

like image 33
BenKoshy Avatar answered Sep 27 '22 00:09

BenKoshy