A big reason people love Elm is the compiler’s helpful error messages. The compiler not only points out what’s wrong, but also includes hints for how to fix it.
One of the first error messages you’ll run into is the “Type Mismatch” error. This error is fundamental to Elm’s enforcement of its strict type system and tells you when a function is expecting a different type of argument than the one it has been passed.
In a basic case, this error is straightforward, so first we’ll wrap our heads around what this error is fundamentally about. But in more complex cases, like when you’re passing functions as arguments to other functions, this error can be confusing if you don’t know what to look for.
Type Mismatch Basics
You’ll see the “Type Mismatch” error when, for example, you pass a
instead of an
Int to a function expecting an
Given this function:
showAge : Int -> String showAge age = String.fromInt age
If you called
showAge with a string, like so:
showAge “invalid argument”
You’d get the Type Mismatch error:
TYPE MISMATCH - The 1st argument to `showAge` is not what I expect: Html.text (showAge "invalid argument") #^^^^^^^^^^^^^^^^^^# This argument is a string of type: #String# But `showAge` needs the 1st argument to be: #Int#
This makes sense – an
Int is not a
String, so the compiler is saying the
wrong type is being passed to the function
showAge. The compiler will not
allow you to pass the wrong types of arguments to a function. We can’t use a
String in place of an
Int, and so the compiler lets us know that we’re using
the wrong type. This error is fundamental to enforcing that arguments passed to
a function are of the correct type.
Complex Type Mismatch Cases
While this error is usually clear, you might run into circumstances where it’s more confusing. This often happens when you are passing functions to other functions. In most Elm code and functional code more broadly, you’ll see functions being passed to other functions quite commonly. This results in a chain of functions that each expect certain types of arguments, and if one argument in the chain is wrong, you’ll get the Type Mismatch error. But it might be hard to see where things are going awry.
Consider this code, where we want to pass a
name and an
age to the
profileDetails function, and then we’ll pass the
profileDetails function to
If we pass only the first argument,
profileDetails, leaving out
age, like so:
userProfile : User -> Html msg userProfile user = div  [ profileDetails user.name ] profileDetails : String -> Int -> Html msg profileDetails name age = div  [ text name, text <| String.fromInt age ]
We get the Type Mismatch Error:
TYPE MISMATCH - The 2nd argument to `div` is not what I expect: 2| div  [ profileDetails user.name ] #^^^^^^^^^^^^^^^^^^^^^^^^^^^^# This argument is a list of type: List #(Int -> Html msg)# But `div` needs the 2nd argument to be: List #(Html msg)# #Hint#: I always figure out the argument types from left to right. If an argument is acceptable, I assume it is “correct” and move on. So the problem may actually be in one of the previous arguments!
Why do we get this error?
The first thing we’d probably look at, given the text of the error message, is
the type annotation of
div expects a
List (Html msg),
we’d double check what
profileDetails is doing. So we look at its type
annonation, and we see – well,
profileDetails results in an
Html msg, and
it’s already in a
List when it’s passed to the
div function. Isn’t that
We’d then look at the hint, which reminds us that the compiler looks at the
arguments left to right, so the first argument to
div here, an empty list,
could also be wrong. But we’re confident that this empty list is an acceptable
first argument here.
The real problem is that
profileDetails is simply missing the second argument
that it expects!
profileDetails expects the user’s name, a
String, and their
Int. So far we’ve only passed
name. All we need
to do is pass the
profileDetails as well.
This now compiles:
userProfile : User -> Html msg userProfile user = div  [ profileDetails user.name user.age ] profileDetails : String -> Int -> Html msg profileDetails name age = div  [ text name, text <| String.fromInt age ]
Why then did the compiler not say, “The arguments to
profileDetails are not
what I expect :
profileDetails expects a
String and an
Int but was only
String”? It might seem like the real problem was in the arguments
profileDetails, not the ones passed to
div. But the compiler
(profileDetails name) as valid in itself, just not as the second
div when in a List. And if the compiler complains about passing
too many arguments to a function, why does it not complain about too few?
The answer has to do with how Elm works, and a concept called “Partial
Application”. When the
name is passed to
profileDetails but not the
we’d say that
profileDetails has been “partially applied”. In Elm, even though
we’d generally say that
profileDetails is a function, implying that it is a
single function, it is in fact a set of functions. The first function takes the
name and returns a function that takes the
age, and that function finally
Html msg. So really it’s two functions. If
took a “Bio”, then it would be three functions under the hood, each one passing
its resulting function to the next, until it produces the final data structure
as represented in the annotation. This process is called “currying”, and it’s
fundamental to how Elm works.
Rule of Thumb
If you see a function in a Type Mismatch error, you’ll want to check that the
function has been passed all its arguments (or the number you’re expecting to
pass it at that point, since you can use partial application deliberately too).
In the above error, where it says the type is:
List #(Int -> Html msg)#, we
can tell that
(Int -> Html msg) represents a function because of how it’s
annotated with the arrow, just like we annotate functions normally. You can
think of it like the function is waiting for an argument, in this case the
Int. Here the function is also in a
List because we’ve put it in
the second list to passed
div  [ profileDetails user.name ]. When you
see a function in the error message, you can likely skip the other debugging
steps and go straight to checking if the function has been partially applied
when you didn’t mean it to be.
While a full examination of currying and partial application is outside the scope of this post, what’s valuable to remember is that each argument to a function in truth represents a distinct function with that argument applied. It therefore must be legal for a function to accept fewer arguments than its total, because that’s what’s already happening each time you pass an argument to a function in Elm. If you come to expect that a function may be passed fewer arguments than its total, when you see a function annotation in the Type Mismatch error you’ll know that this is something to look out for. You might even find yourself using partially applied functions on purpose!