Creating types
Products¶
You can make your own types like this:
- The choice of the names
Square
andSq
are both arbitrary. Both must be capitalized.
This creates a new type Square
, where values of type Square
look like Sq i j
, where i
and j
are Int
s:
> :t Sq 5 4
Sq 5 4 :: Square
> :t Sq 5 True
"Couldn't match expected type ‘Int’ with actual type ‘Bool’"
> :t Sq
Sq :: Int -> (Int -> Square) -- (1)!
> :t Sq 3
Sq 3 :: Int -> Square -- (2)!
-
Actually, the repl will drop the brackets, and show:
Int -> Int -> Square
. -
If this is unclear, see here for more info.
Tip
The type Square
contains the same information as the product type (Int, Int)
. These types are different, in the sense that code which expects one will not work with the other, but it is easy to write loss-less functions between them:
Note
The number of types following Sq
can be 0 or more. For example:
If the number of types is 1
, you will see a suggestion to replace data
with newtype
. See more about newtype
here
Records¶
You can also name entries:
row
and col
are now accessing functions:
> entity = Sq 4 5
> :t entity
entity :: Entity
> row entity
4
> col entity
5
> :t row
row :: Entity -> Int
> :t col
col :: Entity -> Int
Sums¶
Entity
is a type*, butSq
andPlayer
are values belonging to that type
The vertical bar |
indicates that an Entity
is either made with Sq
or with Piece
:
> Sq 4 6
Sq 4 6 :: Entity
> :t Player False
Player False :: Entity
> :t Player
Player :: Bool -> Entity
Note
The type Entity
contains the same information as the type Either (Int, Int) Bool
, and one could write functions between them:
Tip
You can combine products and sums, using your own types:
data ChessPiece = Piece PieceType Color | SquareType Square -- (1)!
data Color = Black | White
data PieceType = Bishop | Rook
data Square = Sq Int Int
- Note that Haskell is unconcerned by the order of definitions: the definition of
Color
comes after its use in the definition ofChessPiece
.
Parameterized types¶
One can also create types which take another type as a parameter:
This creates Piece Bool
, Piece Int
, and so on:
> data Piece c = Bishop c | Knight c | King c
> :t Knight True
Knight True :: Piece Bool
let four = 4 :: Int
> :t King four
King four :: Piece Int
> :t King (King True)
King (King True) :: Piece (Piece Bool)
Note
One can think of Piece
as a function on types, which gives a type c
, produces a type Piece c
.
To make this idea explicit, one can say that Piece
has kind Type -> Type
, where kind is a name for the types that types themselves have.
Recursive types¶
Here, BinTree
is being used recursively in its own definition. Values of BinTree
include:
> data BinTree = Leaf Int | Branch BinTree BinTree
> :t Leaf 4
Leaf 4 :: BinTree
> :t Leaf True
> :t Branch (Leaf 4) (Leaf 5)
Branch (Leaf 4) (Leaf 5) :: BinTree
> :t Branch (Leaf 3) ((Branch (Leaf 6) (Leaf 8)))
Branch (Leaf 3) ((Branch (Leaf 6) (Leaf 8))) :: BinTree
Recursive types can also be parametrized:
data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)
> :t Leaf True
Leaf True :: BinTree Bool
> :t Leaf ()
Leaf () :: BinTree ()
> :t Branch (Leaf True) (Leaf False)
Branch (Leaf True) (Leaf False) :: BinTree Bool
> :t Branch (Leaf True) (Leaf ())
"Couldn't match expected type ‘Bool’ with actual type ‘()’" -- (1)!
- The definition of
Branch
requires that the left and right branch be trees of the same type, which is why this doesn't work.
Here is a more complex recursive type and a program of that type:
machine :: Machine Int Int
machine = machine1 where
machine1 :: Machine Int Int
machine1 = M (\i -> (i, if i > 10 then machine2 else machine1))
machine2 :: Machine Int Int
machine2 = M (\i -> (0, machine2))
Note
The list type can be defined recursively in this way:
In fact, the [a]
type in Haskell is defined in this way, with the [1,2,3]
being extra syntax for convenience.
Synonyms¶
One can also give new names to existing types:
This can be useful for readability, particularly for quite complex types:
Created: January 7, 2023