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.