Creating types in Haskell

In most programming language we need ways to declare our own types. These help with readability, modularity etc. Haskell is no different in offering these capabilities.

We can create a simple data type using the data keyword, followed by either of available values (in C# this would be similar to an enum). So here’s the definition of a Boolean type

data Bool = False | True

The values after the = are the value constructors with the | being like an or, hence this Bool have either False or True value.

Note: The type name and the value constructor must be capital cased.

We can also declare types more like structs/classes in that we can define the field types that make up the type. For example here’s a Point type

data Point = Point Integer Integer

In this example we could create a point as follows

pt = Point 1 2

Accessing these values can be a little verbose (especially when we might have lots of fields) because of the lack of any names, hence we’d use pattern matching, i.e.

xpt :: Point -> Integer
xpt (Point x _) = x

-- example of printing the x value of value pt of type Point
print (xpt pt)

Note: The type annotation is not required and the underscore is a discard, hence is ignored.

We can combine types just as we combined False and True into essentially a union. So for example here we have a data type Shape with constructors for Circle and Triangle.

data Shape = Triangle Int Int Int | Circle Int

triangle = Triangle 1 2 3
circle = Circle 4

It’s not clear what these fields of the constructors mean, so let’s add some names which also allows us to more easily access specific values from the data (i.e. fixes the issue with using pattern matching – which ofcourse is still a valid way of doing things).

data Shape = Triangle { 
   hypotenuse :: Int, 
   opposite :: Int, 
   adjacent :: Int 
} | Circle { 
  radius :: Int 
}

We can declare instances of this type using names or without names, for example

-- not using names
triangle = Triangle 1 2 3
-- with names
triangle1 = Triangle { opposite = 5, hypotenuse = 6, adjacent = 7 }

Instead of creating a function to extract a value (like we did with the xpt function we created) we can now use the following

print (hypotenuse triangle)

and Haskell essentially creates the function for us, i.e. using the REPL along with :t hypotenuse we get the following

hypotenuse :: Shape -> Int

Haskell is immutable, so how do we make changes to an instance of data? Well we “copy and update”. Thankfully Haskell makes this easy (so we don’t have to literally create copies of every field on our data and then change values).

If you’ve used JavaScript it’s like using a spread operator of in F# and with.

newTriangle = triangle { hypotenuse = 10 } 

In this case newTriangle is a copy of the triangle data, with the hypotenuse changed.