Elm Language Type Variables


Example

Type variables are uncapitalized names in type-signatures. Unlike their capitalized counterparts, such as Int and String, they do not represent a single type, but rather, any type. They are used to write generic functions that can operate on any type or types, and are particularly useful for writing operations over containers like List or Dict. The List.reverse function, for example, has the following signature:

reverse : List a -> List a

Which means it can work on a list of any type value, so List Int, List (List String), both of those and any others can be reversed all the same. Hence, a is a type variable that can stand in for any type.

The reverse function could have used any uncapitalized variable name in its type signature, except for the handful of special type variable names, such as number (see the corresponding example on that for more information):

reverse : List lol -> List lol

reverse : List wakaFlaka -> List wakaFlaka

The names of type variables become meaningful only when there when there are different type variables within a single signature, exemplified by the map function on lists:

map : (a -> b) -> List a -> List b

map takes some function from any type a to any type b, along with a list with elements of some type a, and returns a list of elements of some type b, which it gets by applying the given function to every element of the list.

Let's make the signature concrete to better see this:

plusOne : Int -> Int
plusOne x = 
    x + 1

> List.map plusOne
<function> : List Int -> List Int

As we can see, both a = Int and b = Int in this case. But, if map had a type signature like map : (a -> a) -> List a -> List a, then it would only work on functions that operate on a single type, and you'd never be able to change the type of a list by using the map function. But since the type signature of map has multiple different type variables, a and b, we can use map to change the type of a list:

isOdd : Int -> Bool
isOdd x =
    x % 2 /= 0

> List.map isOdd
<function> : List Int -> List Bool

In this case, a = Int and b = Bool. Hence, to be able to use functions that can take and return different types, you must use different type variables.