Types in Haskell
Values have types. And types have kinds. We attach types to values so that we can do static checks at compile time, e.g., make sure that functions are taking the correct sort of arguments.
1. Value Constructors
True
and False
are value constructors for the data type Bool
:
data Bool = True | False
They don't take any arguments so they are called nullary constructors. They can also be thought of as constants.
The value constructors can also be made to take arguments. Circle
and Float
are value constructors for the data type Shape
:
data Shape = Circle Float | Rectangle Float Float
The Circle
constructor takes one argument of type Float
The value constructors can be pattern matched on the left side in functions:
surface :: Shape -> Float surface (Circle r) = 3.14 * r ^ 2 surface (Rectangle w h) = w * h
We can use :t
to get the type of a variable. The type name and value constructors have to be capital cased.
Value constructors are functions and thus have a type.
2. Function Types
The surface
function has type:
surface :: Shape -> Float
A constructor is really a function that produces a value with a type. For example, the type of Circle
is:
Circle :: Float -> Shape
3. Type Variables and Type Constructors
Types can take parameters (cf Java Generic Type). Then, we say that the type is polymorphic or parameterized.
data Maybe a = Nothing | Just a
Maybe
is a type constructor that takes a parameter a
.
One thing that often confuses me is the reuse of variable names.
data Color a = Blue a | Green a | Red a
In the above, our type constructor Color
takes one type parameter that we call a
and each one of our data/value constructors, e.g. Blue
, take one value argument that we also call a
.
We see type constructors when writing signatures. A value can never have a type Maybe
or []
, it needs to be Maybe
something or a list []
of something.
myColor :: Color Bool myColor = Blue False
The first line calls the type constructor. The second line calls the data constructor.
A type constructor has a kind.
4. Type alias
Use type
to declare a type synonym:
type Money = Int
However, the compiler will not track the difference between Money
and Int
. So a function that accepts Int
will still accept Money
5. Type Inference
Hindley-Milner type inference is used to determine the most general type to give each value. So then static type checking can still be done, but without explicit type annotations.
6. Related
Related, is the concept of typeclass, which allows us to put constraints on type regarding the functions that they are required to implement.