We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

Login or registerto boost this post!

Show some love to the author of this blog by giving their post some rocket fuel π.

Login or register to start working on this issue!

Engineers who find a new job through Functional Works average a 15% increase in salary π

Algebraic Data Types are a way for us to define types like the ones that come with Haskell e.g. `Bool` and `Int`.

Single Constructor Without Arguments

The most simple data type we can construct in Haskell is a type with a single constructor,

``````data Frame = MkFrame
x = MkFrame
``````

Examining the type in `ghci`

``````:> :type MkFrame
MkFrame :: Frame

:> :type x
x :: Frame
``````

In this example, we use the `data` keyword to signify a Data Type, `Frame` is the name of that type and we refer to it as a Type Constructor. `MkFrame` is called a Data Constructor and this is how we create new instances of the type `Frame`.

Enums

Enums (Enumeration Types), also referred to as Sum Types allow us to design a type that is reminiscent of a Logical OR. Suppose we want to have a type that will represent a day in the week, the day will be either Saturday, or Monday, or Tuesday, ...etc.

``````data Day = Saturday | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday
day = Tuesday
``````

Examining the type in `ghci`

``````:> :type Saturday
Saturday :: Day

:> :type day
day :: Day
``````

Pattern Matching

We can pattern match on our data type like so:

``````dayNumber :: Day -> Int
dayNumber Saturday  = 1
dayNumber Sunday    = 2
dayNumber Monday    = 3
dayNumber Tuesday   = 4
dayNumber Wednesday = 5
dayNumber Thursday  = 6
dayNumber Friday    = 7
``````

or using a `case of` statement:

``````dayNumberCase :: Day -> Int
dayNumberCase x =
case x of
Saturday  -> 0
Sunday    -> 2
Monday    -> 3
Tuesday   -> 4
Wednesday -> 5
Thursday  -> 6
Friday    -> 7
``````

Constructors with arguments

We can also declare types whose constructor takes one or more arguments, these are commonly referred to as Product Types. Let's say we want to represent a point in the cartesian coordinate system i.e. a point with `x` and `y` coordinates,

``````data Point = P Int Int
mypoint = P 1 2
``````

Examining in `ghci`

``````:> :type P
P :: Int -> Int -> Point

:> :type mypoint
mypoint :: Point

:> :type P 1 2
P 1 2 :: Point
``````

It's a convention in Haskell code to sometimes label the data constructor with the same label as the type constructor, to illustrate this with our `Point` example:

``````data Point = Point Int Int
mypoint = Point 1 2
``````

Record Syntax

Let's say we want to define a `User` type that has the `name` and `age` of a user, we can try to do something like this

``````data User = User String Int
``````

where `name` is of type `String` and `age` is of type `Int`. However, in our application, we will want to get the name and age from an instance of this data type, we can do this like so:

``````getName :: User -> String
getName (User name _) = name

getAge :: User -> Int
getAge (User _ age) = age
``````

but, as you can imagine, this would get too daunting and verbose if our data constructor takes a large number of arguments, and we'll have to modify all these accessor functions if we add or remove arguments from the data constructor. Also, we have to keep in mind the position of each argument. Enter records, records allow us to write:

``````data User = User
{ name  :: String
, age   :: Int
}

-- Create a User
user1 = User
{ name = "Oliver Heaviside"
, age = 74
}

-- This still works
user2 = User "James Clerk Maxwell" 48
``````

``````name :: User -> String
age :: User -> Int
``````

We also get a nice field update syntax

``````changeAge user newAge = user { age = newAge}

user1 = User
{ name = "Oliver Heaviside"
, age = 74
}

newuser = changeAge user1 42
``````

Record Downsides

Unfortunately records have some issues, refer to this for more details.

Parametrized Types

Type constructors can be parametrized with types, the standard `Prelude` library defines the `Maybe` type:

``````data Maybe a = Nothing | Just a
``````

Here, `Maybe` is parametrized with the generic type `a` which can take on any type, this is a bit like Generics in other languages. The `Maybe` type allows us to express the nonexistence of a value, for example when trying to take the head of a list, instead of erroneously applying head to an empty list, we can defend against this:

``````lhead :: [a] -> Maybe a
``````

And, playing around in `ghci`

``````:> :type lhead []

lhead [1] :: Num a => Maybe a
``````

Recursive Types

Haskell allows us to define types in terms of themselves i.e. recursive types. Let's illustrate this by making a type for a Binary Tree:

``````data BinTree a = EmptyTree
| Node a (BinTree a) (BinTree a)

mytree = Node 1 EmptyTree EmptyTree
``````

Here, we constructed `mytree` to be a tree with a single node, with an empty left and right subtrees. We can also construct a tree with 3 nodes:

``````mytree2 = Node 1 (Node 2 EmptyTree EmptyTree) (Node 3 EmptyTree EmptyTree)
``````

Originally published on muattiyah.com