UP | HOME

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.

7. Sources

8. useful inks

Created: 2024-07-15 Mon 01:28