Until now, basic types have been used in the examples and worksheets. Those where:

  • Bool: True, False
  • Int: -2^63 to 2^63-1
  • Integer: -infty to infty
  • Char: a-Z, 0-9, \n, \t, …
  • Float: -infty to infty, single precision
  • Double: -infty to infty, double precision
  • Tuples
  • Lists
  • Functions

Algebraic Data Types

In Haskell new data types can be created. They are also called “algebraic data types”. The syntax is as follows:

  data <type> = <constructor> <comp> <comp> ...
            | <constructor> <comp> <comp> ...
            | ...
            deriving (Show, ...)

-- Example:
data BookInfo = Book Int String deriving (Show)
data BillingInfo = CreditCard Float String String
                 | Invoice Float Int
                 deriving (Show)

b = Book 123 "Haskell"
c = CreditCard 1234.56 "John Doe" "213123ABC"
i = Invoice 123.45 14

-- Pattern Matching:
title :: BookInfo -> String
title (Book _ t) = t

amount :: BillingInfo -> Float
amount (CreditCard a _ _) = a
amount (Invoice a _) = a
  

The following parts are required:

  • The Type Constructor defines the name of the new type
  • The Constructor is used to create a value of the type (multiple constructors are possible)
  • The Components define field types
  • The Deriving Clause automatically derives instances for the named classes (multiple classes are possible)

Functions can access components via pattern matching.

type vs data

The type keyword can be used to create a type alias.

  • Pro: functions for already existing types can be reused
  • Con: no protection from mixing up “incorrect” types (see examples)
  type Student = (String, Int)
email :: Student -> String
email s = fst s
email ("[email protected]", 24) -- ~> "[email protected]"
email ("MacBook", 999) -- ~> "MacBook"
  

The data keyword can be used to create a new type:

  data Student = Student String Int deriving (Show)
email :: Student -> String
email (Student e _) = e
email (Student "[email protected]" 24) -- ~> "[email protected]"
email ("MacBook", 999) -- Error: "MacBook" is not a Student
  

Record Syntax

The record syntax can be used to define named fields:

  -- Instead of writing:
data PersonType = Person String String String Int deriving (Show)
-- Which String represents what?
-- Is the first string the first name or the last name or something completely different?
-- Is the Int the age or the year of birth?

-- Use:
data PersonType = Person {
    firstName :: String,
    lastName :: String,
    email :: String,
    yob :: Int
} deriving (Show)

-- Record syntax automatically creates functions to access the fields:
firstName (Person "Haskell" "Curry" "unknown", 1900) -- ~> "Haskell"
lastName (Person "Haskell" "Curry" "unknown", 1900) -- ~> "Curry"

-- Also allows creation with named components (order does not matter):
p = Person {
    lastName = "Curry",
    firstName = "Haskell",
    email = "unknown",
    yob = 1900
}
  

Polymorphic Data Types

Data types can be polymorphic. This means that they can be used with different types.

  -- Validation framework
data ValidatedString = Ok String | Nok String -- Validating email
validateEmail :: String -> ValidatedString

data ValidatedInt = Ok Int | Nok String -- Validating age
validateAge :: Int -> ValidatedInt

-- Repeated code, could this be written better? Of course:
data Validated a = Ok a | Nok String
validateEmail :: String -> Validated String
validateInt :: Int -> Validated Int
  

Recursive Data Types

Recursive data types are data types that include a reference to themselves. Examples for recursive data types are linked lists and trees.

  -- List
data List a = Empty | Cons a (List a) deriving (Show)
-- Constructing a list:
l = Cons 1 (Cons 2 (Cons 3 Empty))

listSize :: List a -> Int
listSize Empty = 0
listSize (Cons _ xs) = 1 + listSize xs

-- Tree
data FS = Folder String [FS] | File String deriving (Show)
-- Constructing a tree:
fs = Folder "root" [
    File "file1.txt",
    Folder "folder1" [
        File "file2.txt",
        File "file3.txt"
    ]
]

allFiles :: FS -> [FS]
allFiles (File f) = [File f]
allFiles (Folder _ fs) = concatMap allFiles fs