Beginner thought process on type mismatch error

Hi all!

I’m a beginner Elm programmer. I saw elm-ui mentioned on Hacker News lately, and I wanted to try it. As I built a toy page, I stumbled upon a type mismatch error that I had difficulty understanding, but which I ultimately managed to resolve by myself.

The type mismatch error was:

-- TYPE MISMATCH ------------ /Users/francois/Projects/elm/scoutges/src/Main.elm

Something is off with the body of the `searchView` definition:

111|>    row []
112|>        [ search []
113|>            { text = queryString model
114|>            , placeholder = Just (placeholder [] (text "Type your query..."))
115|>            , label = labelHidden "Search"
116|>            , onChange = \q -> SearchFor q
117|>            }
118|>        ]

This `row` call produces:

    Element Msg

But the type annotation on `searchView` says it should be:

    Element msg

in the following code:

module Main exposing (..)

import Browser
import Browser.Navigation as Nav
import Element exposing (Element, alignRight, alignTop, centerY, el, fill, link, padding, rgb255, row, spacing, text, width)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Element.Input exposing (labelHidden, placeholder, search)
import Html exposing (Html)
import Url



-- MAIN


main : Program () Model Msg
main =
    Browser.application
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        , onUrlChange = UrlChanged
        , onUrlRequest = LinkClicked
        }



-- MODEL


type alias Model =
    { key : Nav.Key
    , url : Url.Url
    , query : Maybe String
    }


init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
    ( Model key url Nothing, Cmd.none )



-- UPDATE


type Msg
    = LinkClicked Browser.UrlRequest
    | UrlChanged Url.Url
    | SearchFor String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        LinkClicked urlRequest ->
            case urlRequest of
                Browser.Internal url ->
                    ( model, Nav.pushUrl model.key (Url.toString url) )

                Browser.External href ->
                    ( model, Nav.load href )

        UrlChanged url ->
            ( { model | url = url }
            , Cmd.none
            )

        SearchFor q ->
            ( { model | query = Just q }
            , Cmd.none
            )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none



-- VIEW


view : Model -> Browser.Document Msg
view model =
    { title = "URL Interceptor"
    , body =
        [ Element.layout [] (navBarView model) ]
    }


navBarView : Model -> Element msg
navBarView model =
    row []
        [ el [ alignRight ] (searchView model)
        ]


searchView : Model -> Element msg
searchView model =
    row []
        [ search []
            { text = ""
            , placeholder = Just (placeholder [] (text "Type your query..."))
            , label = labelHidden "Search"
            , onChange = \q -> SearchFor q
            }
        ]

Initially, I suspected that the problem stemmed from the onChange lambda. It was the only thing in searchView that dealt with Msg at all. search's type is:

search :
    List (Attribute msg)
    -> { onChange : String -> msg
       , text : String
       , placeholder : Maybe (Placeholder msg)
       , label : Label msg
       }
    -> Element msg

When I changed my code to:

searchView : Model -> Element msg
searchView model =
    row []
        [ search []
            { text = ""
            , placeholder = Just (placeholder [] (text "Type your query..."))
            , label = labelHidden "Search"
            , onChange = searchQuery
            }
        ]


searchQuery : String -> msg
searchQuery q =
    SearchFor q

then I received a different type mismatch error:

-- TYPE MISMATCH ------------ /Users/francois/Projects/elm/scoutges/src/Main.elm

Something is off with the body of the `searchQuery` definition:

123|     SearchFor q
         ^^^^^^^^^^^
This `SearchFor` call produces:

    Msg

But the type annotation on `searchQuery` says it should be:

    msg

As I was writing this message to ask for help, and reading examples on elm-ui, I thought of using this:

searchView : Model -> Element msg
searchView model =
    row []
        [ search []
            { text = ""
            , placeholder = Just (placeholder [] (text "Type your query..."))
            , label = labelHidden "Search"
            , onChange = SearchFor
            }
        ]

But this returned the same type error as the 1st attempt.

I both FAQ entries What’s the difference between Html Msg and Html msg? and What’s the difference between Cmd Msg and Cmd msg?. I can read some Haskell, and I believe I understand type “holes”, but I really, really didn’t understand what was the problem in this code.

Re-reading the elm-ui page for Element.Input, I found the following example for checkboxes:

import Element exposing (text)
import Element.Input as Input

type Msg
    = GuacamoleChecked Bool

view model =
    Input.checkbox []
        { onChange = GuacamoleChecked
        , icon = Input.defaultCheckbox
        , checked = model.guacamole
        , label =
            Input.labelRight []
                (text "Do you want Guacamole?")
        }

To my untrained eye, this was the same as my last example. This example does not have a type annotation, but even then, it is the example that unblocked me. I changed my type annotations from:

searchView : Model -> Element msg

to

searchView : Model -> Element Msg

throughout my code. Lo and behold, it finally worked! My code is a straight copy/paste of Web Apps Navigation Example, modified to add some UI elements.

I think that when I wrote new functions, I used the following type annotation as my guide:

viewLink : String -> Html msg

This is the type annotation that is written in the Navigation Example.

I was also confused by the 1st error message I received:

-- TYPE MISMATCH ------------ /Users/francois/Projects/elm/scoutges/src/Main.elm

Something is off with the body of the `searchView` definition:

111|>    row []
112|>        [ search []
113|>            { text = queryString model
114|>            , placeholder = Just (placeholder [] (text "Type your query..."))
115|>            , label = labelHidden "Search"
116|>            , onChange = \q -> SearchFor q
117|>            }
118|>        ]

This `row` call produces:

    Element Msg

But the type annotation on `searchView` says it should be:

    Element msg

In my mind, I expected searchView's values of type Msg to be able to be “assigned” to msg. I believe that this is the root cause of my misunderstanding.

I don’t have a real idea about what should change in that error message to make it clearer. Re-reading it after banging my head on this for the past hour, it looks obvious now. At the time, it didn’t.

Thanks for your time and have a great day!
François

$ elm --version
0.19.1
4 Likes

Well, as you probably know by now, Msg is the name of a specific, custom type, whereas msg is a unbound type variable (I think that’s what it’s called).
The confusion might stem from the “convention” of these two having the same name, with only the casing of the first letter being different.
I remember that I tripped over the same problem several times. Even though I think the convention of having a Model and a Msg type in Elm makes sense once you see through the Matrix, it can be confusing if the semantics of these types isn’t fully understood yet.

What I actually did (and still do sometimes) in my code is not necessarily using just Msg and Model, especially if I reuse existing code (rename refactoring ftw).
Instead, I try to come up with names that are more meaningful to me, to have richer semantics associated with the names. As for unbound type variables, like the lower-case msg in your case, I used a prefix at the beginning, e.g. ubMessage.

This might help the eye to see the kind of discrepancy you had described in your example, at least I profited.

As for the error message: would it have helped you if the compiler would have “said”, additionally:

Msg is a custom type, whereas msg is an unbound type variable. Maybe you want the type signature to actually read Msg?

I can see how Evan already contemplated to add something like that, but decided against it for a good reason I’m not aware of.

No, I’m fine with the existing convention.

searchView : Model -> Element msg

I expected the above to be the same as if I was “assigning” Msg to msg, that’s all. Elm did point to the right place. I think it means I don’t know as much about types as I thought I did :slight_smile:

Cheers!

This is how interpret the error message:

“In the type annotation, you’ve said that this function can produce any type of message.

But in the function body, I see that this function only ever will produce messages of a type called Msg!

This means either:

  • That you have a misleading type annotation. It says any message, but only produces Msg messages. If so, change the type annotation to Msg instead of msg!
  • That you’ve accidentally used a specific message in this function, but you intended it to be generic. If so, replace SearchFor with something of a generic type!
8 Likes

Wow lydell, this is a great message. Maybe some form of it can be added to core? Thanks a bunch everyone, and have a nice day!

Thank you for reporting this! I 100% think the message you got isn’t good enough. Def should be a Hint or Note for that case!

Can @francois or @lydell put an SSCCE of this in elm/error-message-catalag?

I periodically go through the issues there to improve error messages, and I’d love to improve this particular message in a future release! I know a lot of people get tripped up on msg vs Msg early on, but it had not occurred to me to address it this way.

13 Likes

Done Evan: https://github.com/elm/error-message-catalog/issues/332

3 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.