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