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.