Feedback on the Guide

Going through the guide and very excited about Elm!

There are a few small things that I’ve noticed so far that might cause some confusion for newcomers like myself.

  1. The first two instances of the counter app are not identical to each other. The difference of having type annotations on the functions on the second instance and not the first is probably deliberate, but I would think the set up of the model should be the same in both.

  2. In the Forms sample, the appearance of both “Html Msg” and “Html msg” is confusing. Maybe there should be a note pointing out that “msg” is a type variable? Maybe it should be “a” instead of “msg”? (not sure because maybe “Html msg” is very idiomatic Elm)

  3. In the Type Aliases section, there is a line that says " That would be { name : String, bio : String } -> Bool without the type annotation". I believe this should say “without the type alias”

1 Like

Thanks for the notes! I fixed (3) just now, but I want to ask about the other two.

  1. Can you link to the two instances you are mentioning?
  2. There are two annotations that have Html msg. I’d personally do that in both cases, but I realize it coming before the section on reading types. As someone reading through, what was the nature of the confusion? Was it like 5 minutes or 30 minutes or more of confusion?

Thanks for the quick reply.

  1. Sorry for not being clearer. Instance 1 is in the Introduction under the heading " A Quick Sample" and instance 2 in https://guide.elm-lang.org/architecture/buttons.html
    In the first instance there’s just the following:
    Browser.sandbox { init = 0...
    But in instance 2 there is the explicit setting up of the model

  2. 5 - 10 minute thing, sort of. I proceeded without understanding what the lowercase “msg” really was, but then it clicked later on when I read “Type variables must start with a lower-case letter, but they can be full words.”

I remember when I was starting out with Elm, the difference between Html Msg and Html msg was one of the things that took longest to understand. It was fine because I basically treated it as “magic” until I did understand it, but I’ve often wondered if I’d have grasped the idea quicker if it was Html a.

5 Likes

I can attest to that. Still caches me off guard at this point in time, especially when I’m trying to build a SPA (last week) and needed to realize that I now need to map this views Msg to something the parent can work with.

Good point. To me msg in Html msg is misleading. Msg could also have a different name or could be different than expected. But given what to expect by convention, Html msg should be Html a or Html notMsgCouldBeEverything.

1 Like

For the folks talking about the msg type in this example, it is doing a slightly different thing in each case:

  • In viewInput it is saying that you specify the msg type yourself when you provide the toMsg argument. It is possible to artificially constrain that argument more, but I think it would be somewhat odd to do.
  • In viewValidation it is saying that no messages are produced from that code. It can be placed anywhere and keep working. Again, you can artificially constrain it more, but it would not match with my personal expectations of type annotations.

So I understand that people find this confusing, and I understand that artificially constraining these variables would perhaps get people to just not think about it at that point in the guide. It is also true that the code is written how I would write it in my own code.


To people saying that msg would be better named a or something else, let’s say that change is made. Now the code is not how anyone would recommend writing it, and I suspect you’d have other people asking why it does not match the convention you see in functions like:

Time.every : Float -> (Posix -> msg) -> Sub msg
File.Select.file : List String -> (File -> msg) -> Cmd msg

They would say “shouldn’t the guide be consistent? Is this the real convention? Or should we use a everywhere?” That kind of thing. So I don’t think that path is particularly nice.

2 Likes

I would say “yes, use ‘a’ everywhere.” The msg / Msg concept is very confusing for a newbie like me.

1 Like

That might seem good idea here, but what about when you get to more complex cases like Browser.element? That is currently defined as

element :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Html msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg

Now compare that with how it would look with just a, b and c:

element :
    { init : a -> ( b, Cmd c )
    , view : b -> Html c
    , update : c -> b -> ( b, Cmd c )
    , subscriptions : b -> Sub c
    }
    -> Program a b c

Descriptive type variables are part of documentation. Although in theory there is no difference between type variables msgand c, in practise msg is documenting that here you are meant to use the type customarily known as Msg.

Now let’s take again that earlier example:

Time.every : Float -> (Posix -> msg) -> Sub msg

Even though type variable msg here can take any type, so you could use your Model there if you wanted, that wouldn’t really make any sense as then it wouldn’t work together with your Browser.element. Different types and functions have some interdependent relations which are often documented with descriptive type variables.

2 Likes

Well, it makes me feel better that I wasn’t the only one who got a little mixed up by this. :slight_smile:

The language that I know best is C#, so I’m trying to walk through an analogous scenario with C# generics. Some generics use type names that give no hints about usage/context, while others do. For example, List<T> gives no hints (probably because there are none to be given), but Dictionary<TKey, TValue> does. To me, this bolsters the argument that we should not restrict ourselves to “a”, “b”, “c”, etc and that it is OK for the type variable to add some context. However, I still feel that the coexistence of “Msg” and “msg” in this example is confusing, so that leads me to wonder if “Msg” is the real problem. In C# I would not create a Dictionary where the values are of some type that I created and named “tValue”
var dict = new Dictionary<string, tValue>()
In Elm, would it be better to use something other than “Msg”? Something like “UserFormMsg”?

Yes, I recognize that using generic variable names instead of descriptive ones leads to trading one source of confusion for another. I would find the generic names less confusing in the simple beginner exercises. Either that or explicitly state somewhere early on that wherever msg is used as a variable name the implication is that the variable will be of type Msg even though the function is not restricted in that way.

Perhaps the type variables section of the guide could be built out a bit more. For example, there’s a note:

Note: Type variables must start with a lower-case letter, but they can be full words. We could write the type of List.length as List value -> Int and we could write the type of List.reverse as List element -> List element . It is fine as long as they start with a lower-case letter. Type variables a and b are used by convention in many places, but some type annotations benefit from more specific names.

Perhaps this could be expanded by mentioning Html msg as one of the occasions where a type annotation benefits from a more specific name, and explaining why the convention is helpful in this case? I guess msg is probably the first example of this that most people will see in the wild?

4 Likes

Put more succinctly, does having a Custom Type name that communicates the same level of specificity as a related Type Variable name always lead to confusion?

Always? No.
But is it a common hurdle for beginners? As it seems from other responses in this topic, I’d conclude: Yes. Also, I myself too struggled with this when I started using Elm for the first time.

1 Like

When I’ve taught newcomers to Elm, I usually use Html a to avoid the confusion between msg and Msg.

I usually also start with primitive messages like Html String because I’ve found they help newcomers have a better intuition of what that type parameter in the Html type means.

12 Likes

If our intent is to a> make the type variable be descriptive (so not just “a”) yet b> leave it a variable so that it’s not quite restrictive, then maybe changing the type variable name just enough to give it more distinction from the conventional user type would work?

Perhaps even add a hint of description of why the type variable is not restrictive?

So how about Html probableMsg or Html msgPlus ?

To me, the issue might be that conceptually, msg is actually supposed to stand in for the (unknown) Msg. So the difference doesn’t matter at first.

However, simply stating, when msg is first used, that it is not the same as Msg, but a so-called type variable that will be explained later, which right now is kind of the same as Msg, should mitigate confusion.

3 Likes

Dumb n00b question…
you are telling me that every time I read “msg” it is not supposed to mean any possible kind of variable, but the code is specifically expecting one of the Msg types I have defined?

So if I have…

type Msg = One | Two

…then a function expecting a “msg” is specifically expecting One or Two and nothing else?

I’m serious.
If this is the case… it explains a LOT!
I kept reading this “msg” variables as meaning “messages” in the colloquial sense, and wondering WHAT constitutes a “message” in Elm… I did not make the logical connection that a “message” can only ever be what I define with a type Msg declaration.
Is this correct?

With simple apps that is basically correct. Just to be clear, your type doesn’t actually need to be named Msg, that is just a convention. Whatever type you use in your init/update/view functions as your “message type”, is what is expected when you see msg.

It gets a bit more complicated with more complex apps where you can have types like

type Msg
    = MainMsg
    | OtherMainMsg
    | SubMsg SubMsg

type SubMsg
    = SomeOtherMessage
    | YetAnotherMessage

and msg could mean either Msg or SubMsg depending on where exactly you are in you app code.

No, I think I confused you, sorry! I would like to expand on malaire’s answer.

Anything in a type declaration that is lower case is a type variable. It can, as you initially suspected, be anything. You could put a Maybe String into a function msg -> List msg. That’s totally possible!

The point is that in case of e. g. the update function, it doesn’t really make sense to put there anything other than your Msg type. That is just a really sensical thing to do. That’s why you hint with the type variable name that you should put a message type there.

So practically, it doesn’t really make sense to put something else. But it is in fact just a type variable with no restriction in and of itself.

The reason update uses a type variable for msg is that it doesn’t know you Msg type. (Luckily, it doesn’t have to.) In order for update to know Msg, it would have to import your module! That won’t work.
So instead, it just leaves it open as to what exactly the type of the msg is. That’s your choice and you decide by using e. g. update with your own Msg type.