SlideShare a Scribd company logo
1 of 18
Download to read offline
Category Theory’s Role in Haskell
Prepared for the University of the West Indies
Jared Windover
University of Waterloo
Ontario, Canada
jaredwindover@gmail.com
April 15, 2015
Abstract
Haskell is a functional programming language that has ties to cate-
gory theory, an area of pure mathematics. The purpose of this paper is
to elucidate the nature of that connection, and to what extent an un-
derstanding of Haskell is informed or improved by an understanding of
category theory. A primer on category theory is presented, explaining the
main notions and style of argument along with some elementary results.
A primer on some fundamental ideas in Haskell as well as its syntax is also
presented. The shared notions of currying, functor, monoid and monad
are then examined within both contexts and compared. Motivations and
implications of these structures in Haskell are discussed, with particular
attention paid to the role of monads.
Summary
Acknoledgment 1
1 Introduction 1
2 Category Theory Primer 1
2.1 Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.1.1 Commutative Diagrams . . . . . . . . . . . . . . . . . . . 2
2.2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 Injectivity and Surjectivity . . . . . . . . . . . . . . . . . . . . . 3
3 Haskell Primer 5
3.1 Basic Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.1.1 Type Signatures . . . . . . . . . . . . . . . . . . . . . . . 5
3.1.2 Type Classes . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 Lazy Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2.1 Infinite Lists . . . . . . . . . . . . . . . . . . . . . . . . . 7
4 Currying 8
4.1 Currying in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Comparison of Currying . . . . . . . . . . . . . . . . . . . . . . . 9
5 Functors 9
5.1 Functors in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2 Comparison of Functors . . . . . . . . . . . . . . . . . . . . . . . 11
6 Monoids 11
6.1 Monoids in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6.2 Comparison of Monoids . . . . . . . . . . . . . . . . . . . . . . . 12
7 Monads 12
7.1 Monads in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7.1.1 Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7.1.2 IO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.2 Comparison of Monads . . . . . . . . . . . . . . . . . . . . . . . . 14
8 Conclusion 15
References 16
ii
Acknowledgment
I would like to thank Professor Jonathon Funk for suggesting this research
project, meeting with me to discuss my progress and difficulties, and spending
his time introducing me to category theory. This paper would not have been
possible without his assistance.
1 Introduction
Haskell is a programming language which has steadily risen in popularity since
its birth in the 1980’s. It can be intimidating for beginners, however. Many of
the fundamental concepts in Haskell were inspired by ideas in category theory.
To somebody coming across these ideas for the first time, it is unclear whether
the ideas are difficult to understand because the individual lacks the necessary
grounding in category theory, or because they are unfamiliar ideas that must
be grappled with. The beginner in graphics programming or game design may
benefit from developing a solid base in linear algebra before returning to their
study of graphics, and if they do not choose to do that, they will still learn
a considerable amount of linear algebra as they go. The question, then, is
to what extent Haskell and category theory are analagously coupled. Does one
necessarily form the basis for the other, are they separate conceptual frameworks
that share terms, or is it somewhere in between?
2 Category Theory Primer
To begin, an introduction to the basic objects and methods of category theory is
presented. It is in no way intended to be a complete introduction (and would be
woefully incomplete if it were), however it is intended to give a sense of the field.
Later, when the interplay of ideas in category theory and Haskell is discussed
it should be enough to understand the arguments made.
2.1 Basic Concepts
Category theory is, in some sense, the theory of isomorphism. Isomorphism is
an important concept in modern mathematics which occurs in many different
areas. To say two things are isomorphic is in some way saing that they have
the same structure. A non-mathematical example of this is lowercase letters
and uppercase letters. They are different things, serving different purposes and
being appropriate in different contexts. There is, however, a common struc-
ture between them. One aspect of this is the ordering on the lowercase and
uppercase letter. Letters have precedent and/or antecedent letters. Using the
obvious mapping between uppercase and lowercase letters (take the uppercase
or lowercase of the same letter, e ↔ E, h ↔ H), this structure is preserved.
If there is a lowercase letter with a precedent (e.g. e, preceded by d) and the
uppercase version of its precedent is taken, (D), the result is the same as taking
1
the precedent of the uppercase version (e.g. e → E → D). This idea can be
made explicit with a commutative diagram.
2.1.1 Commutative Diagrams
Example 2.1.
e d
E D
precedent
upper upper
precedent
The way to interpret a commutative diagram is that any path of arrows
from one point to another is equivalent to any other path between those points
(provided that one of the paths has more than one arrow.) So for the above
diagram, taking e’s precedent, d, and then d’s upper, D, is equivalent to taking
e’s upper, E, and then E’s precedent, D. This can be generalized if l is allowed
to represent the set of lowercase letters, and U is allowed to represent the set of
uppercase letter.
l l
U U
precedent
upper upper
precedent
That is to say, precedent and upper commute under composition, or
precedent ◦ upper = upper ◦ precedent
By reformulating notions from one context into category theory’s language
of arrows gives new ways of examining problems, and understanding their struc-
tures.
2.2 Properties
Category theory, in general, is concerned with two types of things: objects and
arrows. Objects can be anything, and are often (though not always) members
of a set. In the above case, there are two objects: the set of lowercase letters
and the set of uppercase letters. Arrows have a head and a tail, but may not
be fully determined by their head and tail. Two different arrows may have the
same head and tail. An example is the antecedent arrow. It also has head and
tail l, but is not identical to the precedent arrow. Also, whenever the following
diagram exists within a category:
A B C
f g
(1)
then the arrow h must also exist as:
2
A B C
f
h
g
(2)
For any two arrows such that the head of one is the tail of the other, the
composition of those arrows must also be an arrow in the category. In particular,
for the example above to be a category, the arrows (precedent ◦ upper) and
(upper ◦ precedent) would also have to exist. Lastly, for any object x in the
category, there must be an arrow idx with head and tail x, such that idx ◦a = a
and b ◦ idx = b for any arrows a and b with head and tail, respectively, x. So,
any object has an identity arrow that acts as a left and right identity under
composition with other arrows.
2.3 Injectivity and Surjectivity
The notions of injectivity and surjectivity can be captured elegantly in a cat-
egory theoretic way. A function, f : X → Y is injective iff f(x) = f(y) =⇒
x = y, and is surjective if ∀y ∈ Y ∃x ∈ X : f(x) = y, or to put it differently,
f(X) = Y .
Theorem 2.1. f : X → Y is injective iff ∀g, h : Z → X, f ◦ g = f ◦ h =⇒
g = h.
Proof: Suppose that f is injective. Consider two functions g, h : Z → X.
Suppose that f ◦g(z) = f ◦h(z) ∀z ∈ Z, denoted more compactly as f ◦g = f ◦h.
Let z ∈ Z. Then
f ◦ g(z) = f ◦ h(z) =⇒ f(g(z)) = f(h(z))
=⇒ g(z) = h(z)
since f is injective.
Now, suppose that f is not injective. Then ∃x1, x2 ∈ X : f(x1) = f(x2), x1 =
x2. Define the following functions:
g : {0} → {x1, x2} : (0) → x1
h : {0} → {x1, x2} : (0) → x2
Then f ◦ g(0) = f(x1) = f(x2) = f ◦ h(0). So f ◦ g = f ◦ h but g = h.
Theorem 2.2. f : X → Y is surjective iff ∀g, h : Y → Z, g ◦ f = h ◦ f =⇒
g = h.
3
Proof: Suppose that f is surjective. Consider two functions, g, h : Y → Z such
that g ◦ f = h ◦ f. Let y ∈ Y . Then ∃x ∈ X such that f(x) = y. So
g(y) = g(f(x))
= g ◦ f(x)
= h ◦ f(x)
= h(f(x))
= h(y)
Thus, g = h.
Now suppose that f is not surjective. Then ∃y0 ∈ Y , such that there is no
x ∈ X with f(x) = y0. Define the following two functions:
g : Y → {0, 1} : (y) → 0
h : Y → {0, 1} : (y) →
1 y = y0
0 otherwise
Then let x ∈ X.
g ◦ f(x) = g(f(x))
= g(y) : y = y0
= 0
= h(y) : y = y0
= h(f(x))
= h ◦ f(x)
So g ◦ f = h ◦ f but h = g.
With the preceding two theorems in hand, injectivity and surjectivity can
be characterized in the following manner:
f : X → Y is injective if
Z X Y
g
h
f
implies that g = h.
f : X → Y is surjective if
X Y Z
f g
h
implies that g = h. Thus, while the element-wise definitions do not make it
clear the relationship between injectivity and surjectivity, or even that there is
any kind of symmetry between them, the category theoretic definition demon-
strates this symmetry elegantly [1]. In fact, in category theory injectivity and
surjectivity are known as dual properties.
4
3 Haskell Primer
Haskell is an open-source, almost purely-functional programming language [7].
It was born in the late 1980’s out of a desire for a common language for re-
searchers to use in experiments on lazy evaluation [4]. Lazy evaluation was a
new idea at the time, that calculations can and should in some cases only be
performed when it is demonstrably necessary to do so. Haskell is a declarative
language, meaning that what something is is specified, rather than a procedure
for calculating it, and the details of performing the calculation follow naturally.
Haskell being almost purely functional means, roughly, that a function’s return
value is independent of context. All that it may depend upon are the values
that are passed to it. It may also be useful to think of Haskell’s identifiers more
like mathematical identifiers, rather than typical language constructs. The in-
vocation of a mathematical function does not change depending on the context,
or between subsequent invocations. Similarly, functions in a purely functional
language will always return the same value dependent only on their parameters.
It was found, however, that Haskell would be more useful if the purely func-
tional abstraction was broken in a particular way (the introduction of the IO
Monad), and this is why it is only almost purely functional. The language was
named after Haskell Curry, an American mathematician whom the founders of
the language felt had made significant contributions to their respective areas of
study [4].
Haskell is a compiled language, for which several open source compilers exist.
A popular choice is ghc (Glasgow Haskell Compiler). There is also ghci, a
command line REPL for Haskell. In any following code snippets that begin
with ghci>, it is implied that this snippet is taken from an interactive session.
3.1 Basic Syntax
While a “Hello World” program can often be instructive, in an introduction to
Haskell, it may be more confusing than anything. Instead, near the end a “Hello
World” program will be examined.
3.1.1 Type Signatures
Functions in Haskell are not required to have a type signature, if the type signa-
ture can be inferred. A type signature can help the code to self-document, even
when it is not needed, and it is common practice to include it [7]. The signature
of a function gives important information about its usage, and combined with
the name can often be enough information to understand what the function
does. As an example of this, consider the type signature of id:
Example 3.1.
id :: a -> a
5
This type signature is interpreted as, regardless of what type is passed to
id, id will return a value of the same type. There is in fact only one function
which will reliably do that, given that the function cannot have knowledge of
what types are possible, and it is difficult to determine the type of an argument
at runtime. This is the identity function which returns the argument it is given.
We can, in fact, infer its implementation from its type signature!
id :: a -> a
id x = x
A function can also return a function.
Example 3.2.
applyTwice :: (a -> a) -> (a -> a)
This function takes an endofunction (a function from a type to the same
type) and returns a new endofunction on the same type. The implementation
is:
applyTwice f x = f $ f x
Note the use of the $ operator. In Haskell, expressions bind to the left. So
the expression f f x is equivalent to (f f) x. To write that function correctly
using parentheses would require f (f x). The dollar sign, forces everything to
the right of it to be evaluated before it is passed to the left. So f $ x y z
is eqivalent to f (x y z) and f $ x $ y $ z is equivalent to f (x (y (z))).
Thus, the expression f $ f x is equivalent to f (f x).
We can take this idea further to implement function composition explicitly:
Example 3.3.
comp :: (b -> c) -> (a -> b) ->(a -> c)
comp f g x = f $ g x
This is a function which takes two functions to return a third, the third
being the composition of the first two. It is already defined in Haskell’s standard
libraries as (.). So the above could be written (redundantly) as:
comp f g = f.g
The following function uses recursion and pattern matching:
Example 3.4.
fib :: Integer -> Integer
fib 0 = 1
fib n = (fib $ n - 1) + (fib $ n - 2)
6
This is a Haskell function for calculating the nth fibonacci number. fib
takes an Integer as argument and also returns an Integer. Next, the program
takes advantage of Haskell’s pattern matching facilities to define the result of
fib for two cases. If the argument to fib is 0, the result is 1. If the argument to
fib is anything else, then fib is defined recursively as the sum of the previous
two fibonacci numbers.
3.1.2 Type Classes
Type classes are similar to interfaces in other language [7]. They allow guaran-
tees to be made about types that are not as strict as specifying the type (instead,
the guarantee is that the type is a member of a type class). An instructive ex-
ample is considering equality testing. There are lots of types for which it is
meaningful to test whether or not instances of those types are equal. Numbers,
characters and strings jump to mind. A stream type, however, might not be
meaningful to test equality (though it also might). To use == in a function,
it would be useful to know that the type it is being applied to can have the
function applied meaningfully (or at least without errors). One option would be
to define a function for every type for which == is defined. Haskell’s typeclasses,
however, allow for a much more elegant solution. A typeclass specifies a set of
methods that are meaningful to apply to any member of the class. An example
of how the equality typeclass might look is:
Example 3.5.
class Eq a where
(==) :: a -> a -> Bool
With this syntax, a new class is created, Eq for any instance of which, the
function (==) is guaranteed to exist. This is a powerful idea, and gives a mecha-
nism through which contracts can be enforced, while supporting polymorphism.
A type is declared to be a member of a typeclass by writing that it is an
instance of that class, and implementing the methods in the class.
3.2 Lazy Evaluation
Haskell features lazy evaluation, a technique in which code is not evaluated until
it is necessary to do so [4]. In other languages, it is common that an argument
being passed to a function must be fully evaluated before the function can
begin running, even if the argument is not used, or will not be used in this
particular invocation. Haskell, rather, attempts to delay this evaluation until it
is absolutely necessary. This allows for interesting language constructs such as
infinite lists.
3.2.1 Infinite Lists
Some languages, such as Python and Javascript have generators, which are a
generalization of iterators. They are pieces of code that can be called repeatedly
7
to return a sequence of values. Thanks to Haskell’s lazy evaluation, this kind of
idea is trivial to implement:
Example 3.6.
ghci > let naturals = [1..]
ghci > let squares = [ x^2 x <- naturals ]
ghci > squares !! 0
0
ghci > squares !! 1
1
ghci > squares !! 346723
120217532176
4 Currying
It can be shown that if S, T and V are sets, then the set of functions f :
S × T → V is isomorphic to the set of functions g : S → [T → V ] where
[T → V ] is the set of functions from T to V . This can be written formally as
HomSet(S × T, V ) ∼= HomSet(S, [T → V ]) [2].
4.1 Currying in Haskell
All functions in Haskell take exactly one argument [5]. Multi-argument func-
tions are simulated with functions taking exactly one argument and returning
a function (which can take the next argument, and so on). So Haskell func-
tions are curried by default, and intermediate functions can be accessed with
the expected syntax. Even operators can be curried in Haskell.
Example 4.1.
ghci > 3 + 7
10
ghci > (+) 3 7
10
ghci > (+3) 7
10
ghci > map (+3) [1..7]
[4,5,6,7,8,9,10]
Where ordinarily, a lambda expression would be needed, partially evaluating
a function allows for more concise, expressive syntax.
Now consider a trivial function:
Example 4.2.
apply :: (a -> b) -> a -> b
8
In this example, we have an aptly named function apply which will take a
function mapping type a to type b, an object of type a and return an object
of type b. Alternatively, this can be thought of as a function that will take a
function of a certain type and return a function of the same type. The signature
is equivalent to:
apply :: (a -> b) -> (a -> b)
As the name of the function suggests, one function that satisfies this type sig-
nature is the function that simply applies the function to its arguments.
apply :: (a -> b) -> a -> b
apply f x = f x
which can be written more concisely by considering the alternative interpreta-
tion of the type signature as:
apply :: (a -> b) -> (a -> b)
apply f = f
or even more concisely by taking advantage of the built in identity function id:
apply :: (a -> b) -> (a -> b)
apply = id
Note that manual currying can be accomplished with the use of lambda
functions:
apply f = x -> f $ f x
4.2 Comparison of Currying
One of Haskell’s prime strengths is the ability to reason about programs [4].
If a partially evaluated function could behave differently than its unevaluated
counterpart, this would make reasoning more difficult. In languages where par-
tial evaluation is achieved via lambda functions, there is an area where behavior
can change, as its left to the programmer to properly pass arguments through.
Haskell’s form of currying is very similar to the category theory conception, and
to the extent of that similarity, it is able to take advantage of the isomorphism.
This ability to reason about how a function behaves without considering the
context makes it easier to write bug-free code.
5 Functors
Functors are analogous to arrows. In fact, if objects are taken to be categories
(small categories, actually), then letting arrows be functors between those cate-
gories induces a category (the category Cat) [1]. They give a way to map from
one category to another, preserving identity and composition of arrows.
9
Formally, functors are mappings from one category to another which preserve
composition of arrows. Functors map objects in one category to objects in
another, and map arrows in one category to arrows in another such that the
composition of arrows in one category is the composition of their mappings in
the other [1]. This can be expressed with the following commutative diagrams.
A
B
C
f
f◦g
g
=⇒
FA
FB
FC
F f
F (f◦g)
F g
Anywhere that the left diagram appears in the original category, the result of
applying the functor to those arguments must have the structure on the right.
5.1 Functors in Haskell
Example 5.1.
class Functor f where
fmap :: (a -> b) -> f a -> f b
instance Functor [] where
fmap _ [] = []
fmap f (x:xs) = (f x) : fmap f xs
Functors in haskell are instances of a parametric type class [5]. A parametric
type class can be thought of as a type constructor. It is not a concrete type,
but given a concrete type can construct a concrete type. As the above example
suggests, a list is a Functor. A list is not a concrete type class, but a list of
Num’s is a concrete type class. Functors give a way for a function on a type to
be applied to the functor of that type.So, for example, any function that can be
applied to an element of a list can be applied to a list of elements using fmap.
Example 5.2.
ghci > fmap (* 2) [(+ 3 1),(+ 3 2),(+ 3 3)]
[8 ,10 ,12]
ghci > fmap (* 2) $ fmap (+ 3) [1, 2, 3]
[8 ,10 ,12]
In general, if (1) F is a functor, (2) f, g have type signature a -> b, and
b -> c, respectively, and (3) x is of type F a then the following must hold:
fmap g.f x = fmap g $ fmap f x
10
5.2 Comparison of Functors
Haskell functors, being parametric types, construct concrete types from other
concrete types. In this way, they map between Haskell types (making them an
endofunctor on the category Hask) [7]. List, [] takes an Int to create [Int]
or a Bool to create [Bool]. fmap maps functions between initial categories to
functions between the resultant categories. As well, fmap preserves function
composition. Haskell’s implementation of functors is completely analogous to
the category theoretic concept.
6 Monoids
Monoids are sets, X with an associative binary operator, ⊕, such that for any
two elements, x, y ∈ X, x ⊕ y ∈ X. A further conditon is that there is an
identity element e ∈ X, such that e ⊕ x = x = x ⊕ e ∀x ∈ X [2].
A monoid is also a one object category. The arrows of this category corre-
spond to the elements of the monoid, composition in the category is the binary
operation in the monoid and the identity arrow in the category is the identity
element in the monoid [1].
6.1 Monoids in Haskell
Monoids in Haskell, like Functors, are typeclasses, though they need not be
parametric. For example, the set of integers along with multiplication is a
monoid. So is the set of integers along with addition.
Example 6.1.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
Monoids are expected to satisfy the following:
mappend mempty x = x
mappend x mempty = x
mappend x (mappend y z) = mappend (mappend x y) z
Defining the set of integers with addition as a monoid can be done as:
newtype Sum a = Sum a
instance Num a => Monoid (Sum a) where
mempty = Sum 0
mappend Sum x Sum y = Sum (x + y)
So, the identity element is defined to be 0, and appending elements is defined
as addition. Defining the set of integers with multiplication as a monoid can be
done as:
11
newtype Product a = Product a
instance Num a => Monoid (Product a) where
mempty = Product 1
mappend Product x Product y = Product (x * y)
In this case the identity element is 1, while the append operation is multiplica-
tion.
6.2 Comparison of Monoids
Again, the structure of the Haskell monoid very closely mirrors the structure of
the category theory monoid. An identity element is defined, and a binary oper-
ation is defined for the typeclass that constructs new elements of the typeclass.
The concepts are completely analagous. An understanding of one is equivalent
to an understanding of the other.
7 Monads
Monads (also known as triples) can be defined in different (equivalent) ways.
The most instructive definition, for understanding their relationship with Haskell
monads, is the following:
Definition 7.1. A Triple (T, η, µ) consists of a functor T, and two natural
transformations, η and µ such that the following diagrams commute:
T T2
T T3
T2
T T2
T
ηT
idT
µ
T η
idT
T µ
µT µ
µ
(3)
[1]
where a natural transformation is defined:
Definition 7.2. A natural transformation between two functors F and G on a
category is a family of arrows {φx}x∈I, indexed by the objects of the category
such that for any arrow f in the category, the following diagram commutes:
a Fa Ga
b Fb Gb
f
φa
F f Gf
φb
(4)
[1]
12
7.1 Monads in Haskell
The monad typeclass in Haskell is defined as [7]:
Example 7.1.
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
x >>= f = join (fmap f x)
join :: m (m a) -> m a
join x = x >>= id
Thus, a monad in Haskell is a parametric typeclass (like a functor), which
provides three methods: a method for taking the parameter of type X to a
Monad X, a method for taking a Monad X and a function that maps from X to
Monad Y and applying it to the Monad X, and a method for taking a Monad Monad X
and reducing it to a Monad X. It is expected that anything being declared as a
Monad is already declared as a functor, and will, in fact, be a requirement in a
future version of the language standard (originally, Haskell had Monads but not
Functors, hence the confusion [4]). It should be noted that definitions have been
provided for two of the methods, but they are mutually recursive. Thus, it is
sufficient for a programmer instantiating a monad to provide an implementation
for either (>>=) or join to also implement the other. A couple of examples may
be instructive:
7.1.1 Maybe
Example 7.2.
data Maybe t = Just t Nothing
instance Monad Maybe where
return x = Just x
join Nothing = Nothing
join Just Nothing = Nothing
join Just (Just x) = Just x
The maybe monad is used to allow for error conditions to propagate through
Haskell code. A Maybe X will be either a Just X or Nothing. So a Maybe Num
will be either Just x, where x is a Num, or Nothing. Functions applied with
fmap (since Monads are also Functors) will return Nothing if the argument was
nothing, otherwise Just y where y is the result of applying the function to the
argument. So, a series of functions can be applied with fmap, each of which will
automatically propagate Nothing values to the top, or just proceed with their
execution [5].
13
7.1.2 IO
The IO monad is used to allow a function to communicate with the external
world, either by writing to files and devices, or by reading from them. A simple
“Hello World” program can be written as:
Example 7.3.
main = putStrLn "Hello World!"
where the type of putStrLn is
ghci > :t putStrLn
putStrLn :: String -> IO ()
So, putStrLn is a function that takes a String and returns a IO (). The
real power of Monads, however, is in their ability to chain together objects.
main = putStrLn "What ’s your name?" >>
getLine >>=
a -> putStr "Hello , " >>
putStrLn a
The above program will ask for the user’s name, read in the input and then greet
the user. While this is already becoming reminiscent of imperative program-
ming, Haskell introduces some syntactic sugar which drives the point home.
The above program is equivalent to the following:
main = do putStrLn "What ’s your name?"
a <- getLine
putStr "Hello , "
putStrLn a
The above syntax makes complex series of operations very intuitive without
sacrificing power or expressivity [7].
7.2 Comparison of Monads
While (>>=) is more commonly focused on in the definition of a monad, it is
actually join which will provide the more obvious connection to category theory.
Since a Monad must already be a functor, one piece of the mapping between
Haskell’s Monad and the category theory triple is obvious. The Functor in the
triple is the Monad instance itself! The second two mappings are less obvious,
however. return is actually equivalent to η and join is equivalent to µ. This
can be seen by considering the following. Haskell expects the following rules to
hold for any instance of a Monad:
join . fmap join == join . join
join . fmap return == join . return == id
join . fmap (fmap f) == fmap f . join
14
The first rule can be seen to be equivalent to part of the commutative diagram
defining Monads:
T3
T2
T2
T
T µ
µT µ
µ
The second rule is equivalent to the other part of the diagram:
T T2
T
T
ηT
idT
µ
T η
idT
The third rule simply states that for any arrow f : Ta → T2
b, the following
diagram commutes:
T2
a Ta
T3
b T2
b
µa
T f f
µT b
which follows from µ being a natural transformation. Thus, Haskell’s definition
of monad can be seen to be equivalent to category theory’s definition of Monad.
8 Conclusion
Primarily, Haskell has benefited from category theory through its use of currying
as a primitive operation and the inclusion of certain analogous concepts, namely
functors, monoids and monads. Its history reflects their relative importance, as
the Monad typeclass was added before Functors or Monoids were added to the
language, and it also provides the most concrete benefit to Haskell. Things such
as state, io and error conditions were made possible through the use of Monads,
while maintaining a lot of the original benefits of the language’s purity. A future
direction for research is to make explicit any results of category theory that are
being used to Haskell’s benefit, or any results which can be applied to the Hask
category to gain insight.
15
References
[1] Michael A. Arbib. Arrows, Structures and Functors. Academic Press, 1975.
[2] Michael Barr and Charles Wells. Category Theory for Computing Science.
Les Publications CRM, 1999.
[3] Michael Barr and Charles Wells. Toposes, Triples and Theories. 2005. url:
http://www.tac.mta.ca/tac/reprints/articles/12/tr12.pdf.
[4] Paul Hudak et al. A History of Haskell: being lazy with class. 2007. url:
http://research.microsoft.com/en-us/um/people/simonpj/papers/
history-of-haskell/.
[5] Miran Lipova˘ca. Learn You a Haskell for Great Good. url: http : / /
learnyouahaskell.com.
[6] Bryan O’Sullivan, Don Stewart, and John Goerzen. Real World Haskell.
url: http://book.realworldhaskell.org.
[7] The Haskell Programming Language. url: https://wiki.haskell.org/
Haskell.
16

More Related Content

What's hot

Fuzzy logic and fuzzy time series edited
Fuzzy logic and fuzzy time series   editedFuzzy logic and fuzzy time series   edited
Fuzzy logic and fuzzy time series edited
Prof Dr S.M.Aqil Burney
 
Fuzzy Set Theory
Fuzzy Set TheoryFuzzy Set Theory
Fuzzy Set Theory
AMIT KUMAR
 

What's hot (17)

BCA_Semester-I_Mathematics-I_Set theory and function
BCA_Semester-I_Mathematics-I_Set theory and functionBCA_Semester-I_Mathematics-I_Set theory and function
BCA_Semester-I_Mathematics-I_Set theory and function
 
Fuzzy logic
Fuzzy logicFuzzy logic
Fuzzy logic
 
Classical Sets & fuzzy sets
Classical Sets & fuzzy setsClassical Sets & fuzzy sets
Classical Sets & fuzzy sets
 
Algebra 2 Section 2-1
Algebra 2 Section 2-1Algebra 2 Section 2-1
Algebra 2 Section 2-1
 
Lesson 26
Lesson 26Lesson 26
Lesson 26
 
Fuzzy logic and application in AI
Fuzzy logic and application in AIFuzzy logic and application in AI
Fuzzy logic and application in AI
 
The Existence of Approximate Solutions for Nonlinear Volterra Type Random Int...
The Existence of Approximate Solutions for Nonlinear Volterra Type Random Int...The Existence of Approximate Solutions for Nonlinear Volterra Type Random Int...
The Existence of Approximate Solutions for Nonlinear Volterra Type Random Int...
 
Fuzzy Logic
Fuzzy LogicFuzzy Logic
Fuzzy Logic
 
Fuzzy sets
Fuzzy sets Fuzzy sets
Fuzzy sets
 
8709508
87095088709508
8709508
 
Fuzzy Set
Fuzzy SetFuzzy Set
Fuzzy Set
 
FUZZY LOGIC
FUZZY LOGICFUZZY LOGIC
FUZZY LOGIC
 
Fuzzy logic and fuzzy time series edited
Fuzzy logic and fuzzy time series   editedFuzzy logic and fuzzy time series   edited
Fuzzy logic and fuzzy time series edited
 
Thinking with shapes
Thinking with shapesThinking with shapes
Thinking with shapes
 
Fuzzy Set Theory
Fuzzy Set TheoryFuzzy Set Theory
Fuzzy Set Theory
 
FUZZY COMPLEMENT
FUZZY COMPLEMENTFUZZY COMPLEMENT
FUZZY COMPLEMENT
 
First Steps in EL Contraction
First Steps in EL ContractionFirst Steps in EL Contraction
First Steps in EL Contraction
 

Viewers also liked

Development pro forma
Development pro formaDevelopment pro forma
Development pro forma
BHuyton10
 
2015 Annual Report Draft 4.1
2015 Annual Report Draft 4.12015 Annual Report Draft 4.1
2015 Annual Report Draft 4.1
Edie Sachs
 

Viewers also liked (12)

Development pro forma
Development pro formaDevelopment pro forma
Development pro forma
 
2015 Annual Report Draft 4.1
2015 Annual Report Draft 4.12015 Annual Report Draft 4.1
2015 Annual Report Draft 4.1
 
Herramientas web2.0
Herramientas web2.0Herramientas web2.0
Herramientas web2.0
 
resume APdec
resume APdecresume APdec
resume APdec
 
Jennifer Kaberi-ANPPCAN Final Presenation
Jennifer Kaberi-ANPPCAN Final PresenationJennifer Kaberi-ANPPCAN Final Presenation
Jennifer Kaberi-ANPPCAN Final Presenation
 
Recruit Impact
Recruit ImpactRecruit Impact
Recruit Impact
 
LeasePlan's implemententation for the Social Connections event II
LeasePlan's implemententation for the Social Connections event IILeasePlan's implemententation for the Social Connections event II
LeasePlan's implemententation for the Social Connections event II
 
Pneumonia
PneumoniaPneumonia
Pneumonia
 
Paralegal Power Break: Sources of Law (Cases)
Paralegal Power Break: Sources of Law (Cases)Paralegal Power Break: Sources of Law (Cases)
Paralegal Power Break: Sources of Law (Cases)
 
Soalan Sains Tahun 1 (Awal Tahun 2017)
Soalan Sains Tahun 1 (Awal Tahun 2017)Soalan Sains Tahun 1 (Awal Tahun 2017)
Soalan Sains Tahun 1 (Awal Tahun 2017)
 
Evaluation for my music magazine q1
Evaluation for my music magazine q1Evaluation for my music magazine q1
Evaluation for my music magazine q1
 
Evaluation
EvaluationEvaluation
Evaluation
 

Similar to Report

A gentle intruduction to category theory
A gentle intruduction to category theoryA gentle intruduction to category theory
A gentle intruduction to category theory
Jeff Jampa
 
A Study of Permutation Groups and Coherent Configurations
A Study of Permutation Groups and Coherent ConfigurationsA Study of Permutation Groups and Coherent Configurations
A Study of Permutation Groups and Coherent Configurations
John Batchelor
 
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
ArnavBishnoi2
 
Independence Complexes
Independence ComplexesIndependence Complexes
Independence Complexes
Rickard Fors
 

Similar to Report (20)

project
projectproject
project
 
A gentle intruduction to category theory
A gentle intruduction to category theoryA gentle intruduction to category theory
A gentle intruduction to category theory
 
A Study of Permutation Groups and Coherent Configurations
A Study of Permutation Groups and Coherent ConfigurationsA Study of Permutation Groups and Coherent Configurations
A Study of Permutation Groups and Coherent Configurations
 
[SEMINAR] 2nd Tues, 14 May, 2019
[SEMINAR] 2nd Tues, 14 May, 2019[SEMINAR] 2nd Tues, 14 May, 2019
[SEMINAR] 2nd Tues, 14 May, 2019
 
Introduction to set theory by william a r weiss professor
Introduction to set theory by william a r weiss professorIntroduction to set theory by william a r weiss professor
Introduction to set theory by william a r weiss professor
 
Point-free foundation of Mathematics
Point-free foundation of MathematicsPoint-free foundation of Mathematics
Point-free foundation of Mathematics
 
Category Theory made easy with (ugly) pictures
Category Theory made easy with (ugly) picturesCategory Theory made easy with (ugly) pictures
Category Theory made easy with (ugly) pictures
 
01bkb04p.ppt
01bkb04p.ppt01bkb04p.ppt
01bkb04p.ppt
 
continuity of module 2.pptx
continuity of module 2.pptxcontinuity of module 2.pptx
continuity of module 2.pptx
 
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
 
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
 
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
R. Bartle, D. Sherbert - Instructors Manual - Introduction to Real Analysis-J...
 
Introduction to Real Analysis 4th Edition Bartle Solutions Manual
Introduction to Real Analysis 4th Edition Bartle Solutions ManualIntroduction to Real Analysis 4th Edition Bartle Solutions Manual
Introduction to Real Analysis 4th Edition Bartle Solutions Manual
 
Dialectica and Kolmogorov Problems
Dialectica and Kolmogorov ProblemsDialectica and Kolmogorov Problems
Dialectica and Kolmogorov Problems
 
Separation Axioms
Separation AxiomsSeparation Axioms
Separation Axioms
 
Logic for everyone
Logic for everyoneLogic for everyone
Logic for everyone
 
Proof-Theoretic Semantics: Point-free meaninig of first-order systems
Proof-Theoretic Semantics: Point-free meaninig of first-order systemsProof-Theoretic Semantics: Point-free meaninig of first-order systems
Proof-Theoretic Semantics: Point-free meaninig of first-order systems
 
Independence Complexes
Independence ComplexesIndependence Complexes
Independence Complexes
 
A survey on different definitions of soft points: limitations, comparisons and...
A survey on different definitions of soft points: limitations, comparisons and...A survey on different definitions of soft points: limitations, comparisons and...
A survey on different definitions of soft points: limitations, comparisons and...
 
AI Lesson 26
AI Lesson 26AI Lesson 26
AI Lesson 26
 

Report

  • 1. Category Theory’s Role in Haskell Prepared for the University of the West Indies Jared Windover University of Waterloo Ontario, Canada jaredwindover@gmail.com April 15, 2015 Abstract Haskell is a functional programming language that has ties to cate- gory theory, an area of pure mathematics. The purpose of this paper is to elucidate the nature of that connection, and to what extent an un- derstanding of Haskell is informed or improved by an understanding of category theory. A primer on category theory is presented, explaining the main notions and style of argument along with some elementary results. A primer on some fundamental ideas in Haskell as well as its syntax is also presented. The shared notions of currying, functor, monoid and monad are then examined within both contexts and compared. Motivations and implications of these structures in Haskell are discussed, with particular attention paid to the role of monads.
  • 2. Summary Acknoledgment 1 1 Introduction 1 2 Category Theory Primer 1 2.1 Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2.1.1 Commutative Diagrams . . . . . . . . . . . . . . . . . . . 2 2.2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2.3 Injectivity and Surjectivity . . . . . . . . . . . . . . . . . . . . . 3 3 Haskell Primer 5 3.1 Basic Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.1.1 Type Signatures . . . . . . . . . . . . . . . . . . . . . . . 5 3.1.2 Type Classes . . . . . . . . . . . . . . . . . . . . . . . . . 7 3.2 Lazy Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 3.2.1 Infinite Lists . . . . . . . . . . . . . . . . . . . . . . . . . 7 4 Currying 8 4.1 Currying in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 8 4.2 Comparison of Currying . . . . . . . . . . . . . . . . . . . . . . . 9 5 Functors 9 5.1 Functors in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 10 5.2 Comparison of Functors . . . . . . . . . . . . . . . . . . . . . . . 11 6 Monoids 11 6.1 Monoids in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 11 6.2 Comparison of Monoids . . . . . . . . . . . . . . . . . . . . . . . 12 7 Monads 12 7.1 Monads in Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 13 7.1.1 Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 7.1.2 IO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 7.2 Comparison of Monads . . . . . . . . . . . . . . . . . . . . . . . . 14 8 Conclusion 15 References 16 ii
  • 3. Acknowledgment I would like to thank Professor Jonathon Funk for suggesting this research project, meeting with me to discuss my progress and difficulties, and spending his time introducing me to category theory. This paper would not have been possible without his assistance. 1 Introduction Haskell is a programming language which has steadily risen in popularity since its birth in the 1980’s. It can be intimidating for beginners, however. Many of the fundamental concepts in Haskell were inspired by ideas in category theory. To somebody coming across these ideas for the first time, it is unclear whether the ideas are difficult to understand because the individual lacks the necessary grounding in category theory, or because they are unfamiliar ideas that must be grappled with. The beginner in graphics programming or game design may benefit from developing a solid base in linear algebra before returning to their study of graphics, and if they do not choose to do that, they will still learn a considerable amount of linear algebra as they go. The question, then, is to what extent Haskell and category theory are analagously coupled. Does one necessarily form the basis for the other, are they separate conceptual frameworks that share terms, or is it somewhere in between? 2 Category Theory Primer To begin, an introduction to the basic objects and methods of category theory is presented. It is in no way intended to be a complete introduction (and would be woefully incomplete if it were), however it is intended to give a sense of the field. Later, when the interplay of ideas in category theory and Haskell is discussed it should be enough to understand the arguments made. 2.1 Basic Concepts Category theory is, in some sense, the theory of isomorphism. Isomorphism is an important concept in modern mathematics which occurs in many different areas. To say two things are isomorphic is in some way saing that they have the same structure. A non-mathematical example of this is lowercase letters and uppercase letters. They are different things, serving different purposes and being appropriate in different contexts. There is, however, a common struc- ture between them. One aspect of this is the ordering on the lowercase and uppercase letter. Letters have precedent and/or antecedent letters. Using the obvious mapping between uppercase and lowercase letters (take the uppercase or lowercase of the same letter, e ↔ E, h ↔ H), this structure is preserved. If there is a lowercase letter with a precedent (e.g. e, preceded by d) and the uppercase version of its precedent is taken, (D), the result is the same as taking 1
  • 4. the precedent of the uppercase version (e.g. e → E → D). This idea can be made explicit with a commutative diagram. 2.1.1 Commutative Diagrams Example 2.1. e d E D precedent upper upper precedent The way to interpret a commutative diagram is that any path of arrows from one point to another is equivalent to any other path between those points (provided that one of the paths has more than one arrow.) So for the above diagram, taking e’s precedent, d, and then d’s upper, D, is equivalent to taking e’s upper, E, and then E’s precedent, D. This can be generalized if l is allowed to represent the set of lowercase letters, and U is allowed to represent the set of uppercase letter. l l U U precedent upper upper precedent That is to say, precedent and upper commute under composition, or precedent ◦ upper = upper ◦ precedent By reformulating notions from one context into category theory’s language of arrows gives new ways of examining problems, and understanding their struc- tures. 2.2 Properties Category theory, in general, is concerned with two types of things: objects and arrows. Objects can be anything, and are often (though not always) members of a set. In the above case, there are two objects: the set of lowercase letters and the set of uppercase letters. Arrows have a head and a tail, but may not be fully determined by their head and tail. Two different arrows may have the same head and tail. An example is the antecedent arrow. It also has head and tail l, but is not identical to the precedent arrow. Also, whenever the following diagram exists within a category: A B C f g (1) then the arrow h must also exist as: 2
  • 5. A B C f h g (2) For any two arrows such that the head of one is the tail of the other, the composition of those arrows must also be an arrow in the category. In particular, for the example above to be a category, the arrows (precedent ◦ upper) and (upper ◦ precedent) would also have to exist. Lastly, for any object x in the category, there must be an arrow idx with head and tail x, such that idx ◦a = a and b ◦ idx = b for any arrows a and b with head and tail, respectively, x. So, any object has an identity arrow that acts as a left and right identity under composition with other arrows. 2.3 Injectivity and Surjectivity The notions of injectivity and surjectivity can be captured elegantly in a cat- egory theoretic way. A function, f : X → Y is injective iff f(x) = f(y) =⇒ x = y, and is surjective if ∀y ∈ Y ∃x ∈ X : f(x) = y, or to put it differently, f(X) = Y . Theorem 2.1. f : X → Y is injective iff ∀g, h : Z → X, f ◦ g = f ◦ h =⇒ g = h. Proof: Suppose that f is injective. Consider two functions g, h : Z → X. Suppose that f ◦g(z) = f ◦h(z) ∀z ∈ Z, denoted more compactly as f ◦g = f ◦h. Let z ∈ Z. Then f ◦ g(z) = f ◦ h(z) =⇒ f(g(z)) = f(h(z)) =⇒ g(z) = h(z) since f is injective. Now, suppose that f is not injective. Then ∃x1, x2 ∈ X : f(x1) = f(x2), x1 = x2. Define the following functions: g : {0} → {x1, x2} : (0) → x1 h : {0} → {x1, x2} : (0) → x2 Then f ◦ g(0) = f(x1) = f(x2) = f ◦ h(0). So f ◦ g = f ◦ h but g = h. Theorem 2.2. f : X → Y is surjective iff ∀g, h : Y → Z, g ◦ f = h ◦ f =⇒ g = h. 3
  • 6. Proof: Suppose that f is surjective. Consider two functions, g, h : Y → Z such that g ◦ f = h ◦ f. Let y ∈ Y . Then ∃x ∈ X such that f(x) = y. So g(y) = g(f(x)) = g ◦ f(x) = h ◦ f(x) = h(f(x)) = h(y) Thus, g = h. Now suppose that f is not surjective. Then ∃y0 ∈ Y , such that there is no x ∈ X with f(x) = y0. Define the following two functions: g : Y → {0, 1} : (y) → 0 h : Y → {0, 1} : (y) → 1 y = y0 0 otherwise Then let x ∈ X. g ◦ f(x) = g(f(x)) = g(y) : y = y0 = 0 = h(y) : y = y0 = h(f(x)) = h ◦ f(x) So g ◦ f = h ◦ f but h = g. With the preceding two theorems in hand, injectivity and surjectivity can be characterized in the following manner: f : X → Y is injective if Z X Y g h f implies that g = h. f : X → Y is surjective if X Y Z f g h implies that g = h. Thus, while the element-wise definitions do not make it clear the relationship between injectivity and surjectivity, or even that there is any kind of symmetry between them, the category theoretic definition demon- strates this symmetry elegantly [1]. In fact, in category theory injectivity and surjectivity are known as dual properties. 4
  • 7. 3 Haskell Primer Haskell is an open-source, almost purely-functional programming language [7]. It was born in the late 1980’s out of a desire for a common language for re- searchers to use in experiments on lazy evaluation [4]. Lazy evaluation was a new idea at the time, that calculations can and should in some cases only be performed when it is demonstrably necessary to do so. Haskell is a declarative language, meaning that what something is is specified, rather than a procedure for calculating it, and the details of performing the calculation follow naturally. Haskell being almost purely functional means, roughly, that a function’s return value is independent of context. All that it may depend upon are the values that are passed to it. It may also be useful to think of Haskell’s identifiers more like mathematical identifiers, rather than typical language constructs. The in- vocation of a mathematical function does not change depending on the context, or between subsequent invocations. Similarly, functions in a purely functional language will always return the same value dependent only on their parameters. It was found, however, that Haskell would be more useful if the purely func- tional abstraction was broken in a particular way (the introduction of the IO Monad), and this is why it is only almost purely functional. The language was named after Haskell Curry, an American mathematician whom the founders of the language felt had made significant contributions to their respective areas of study [4]. Haskell is a compiled language, for which several open source compilers exist. A popular choice is ghc (Glasgow Haskell Compiler). There is also ghci, a command line REPL for Haskell. In any following code snippets that begin with ghci>, it is implied that this snippet is taken from an interactive session. 3.1 Basic Syntax While a “Hello World” program can often be instructive, in an introduction to Haskell, it may be more confusing than anything. Instead, near the end a “Hello World” program will be examined. 3.1.1 Type Signatures Functions in Haskell are not required to have a type signature, if the type signa- ture can be inferred. A type signature can help the code to self-document, even when it is not needed, and it is common practice to include it [7]. The signature of a function gives important information about its usage, and combined with the name can often be enough information to understand what the function does. As an example of this, consider the type signature of id: Example 3.1. id :: a -> a 5
  • 8. This type signature is interpreted as, regardless of what type is passed to id, id will return a value of the same type. There is in fact only one function which will reliably do that, given that the function cannot have knowledge of what types are possible, and it is difficult to determine the type of an argument at runtime. This is the identity function which returns the argument it is given. We can, in fact, infer its implementation from its type signature! id :: a -> a id x = x A function can also return a function. Example 3.2. applyTwice :: (a -> a) -> (a -> a) This function takes an endofunction (a function from a type to the same type) and returns a new endofunction on the same type. The implementation is: applyTwice f x = f $ f x Note the use of the $ operator. In Haskell, expressions bind to the left. So the expression f f x is equivalent to (f f) x. To write that function correctly using parentheses would require f (f x). The dollar sign, forces everything to the right of it to be evaluated before it is passed to the left. So f $ x y z is eqivalent to f (x y z) and f $ x $ y $ z is equivalent to f (x (y (z))). Thus, the expression f $ f x is equivalent to f (f x). We can take this idea further to implement function composition explicitly: Example 3.3. comp :: (b -> c) -> (a -> b) ->(a -> c) comp f g x = f $ g x This is a function which takes two functions to return a third, the third being the composition of the first two. It is already defined in Haskell’s standard libraries as (.). So the above could be written (redundantly) as: comp f g = f.g The following function uses recursion and pattern matching: Example 3.4. fib :: Integer -> Integer fib 0 = 1 fib n = (fib $ n - 1) + (fib $ n - 2) 6
  • 9. This is a Haskell function for calculating the nth fibonacci number. fib takes an Integer as argument and also returns an Integer. Next, the program takes advantage of Haskell’s pattern matching facilities to define the result of fib for two cases. If the argument to fib is 0, the result is 1. If the argument to fib is anything else, then fib is defined recursively as the sum of the previous two fibonacci numbers. 3.1.2 Type Classes Type classes are similar to interfaces in other language [7]. They allow guaran- tees to be made about types that are not as strict as specifying the type (instead, the guarantee is that the type is a member of a type class). An instructive ex- ample is considering equality testing. There are lots of types for which it is meaningful to test whether or not instances of those types are equal. Numbers, characters and strings jump to mind. A stream type, however, might not be meaningful to test equality (though it also might). To use == in a function, it would be useful to know that the type it is being applied to can have the function applied meaningfully (or at least without errors). One option would be to define a function for every type for which == is defined. Haskell’s typeclasses, however, allow for a much more elegant solution. A typeclass specifies a set of methods that are meaningful to apply to any member of the class. An example of how the equality typeclass might look is: Example 3.5. class Eq a where (==) :: a -> a -> Bool With this syntax, a new class is created, Eq for any instance of which, the function (==) is guaranteed to exist. This is a powerful idea, and gives a mecha- nism through which contracts can be enforced, while supporting polymorphism. A type is declared to be a member of a typeclass by writing that it is an instance of that class, and implementing the methods in the class. 3.2 Lazy Evaluation Haskell features lazy evaluation, a technique in which code is not evaluated until it is necessary to do so [4]. In other languages, it is common that an argument being passed to a function must be fully evaluated before the function can begin running, even if the argument is not used, or will not be used in this particular invocation. Haskell, rather, attempts to delay this evaluation until it is absolutely necessary. This allows for interesting language constructs such as infinite lists. 3.2.1 Infinite Lists Some languages, such as Python and Javascript have generators, which are a generalization of iterators. They are pieces of code that can be called repeatedly 7
  • 10. to return a sequence of values. Thanks to Haskell’s lazy evaluation, this kind of idea is trivial to implement: Example 3.6. ghci > let naturals = [1..] ghci > let squares = [ x^2 x <- naturals ] ghci > squares !! 0 0 ghci > squares !! 1 1 ghci > squares !! 346723 120217532176 4 Currying It can be shown that if S, T and V are sets, then the set of functions f : S × T → V is isomorphic to the set of functions g : S → [T → V ] where [T → V ] is the set of functions from T to V . This can be written formally as HomSet(S × T, V ) ∼= HomSet(S, [T → V ]) [2]. 4.1 Currying in Haskell All functions in Haskell take exactly one argument [5]. Multi-argument func- tions are simulated with functions taking exactly one argument and returning a function (which can take the next argument, and so on). So Haskell func- tions are curried by default, and intermediate functions can be accessed with the expected syntax. Even operators can be curried in Haskell. Example 4.1. ghci > 3 + 7 10 ghci > (+) 3 7 10 ghci > (+3) 7 10 ghci > map (+3) [1..7] [4,5,6,7,8,9,10] Where ordinarily, a lambda expression would be needed, partially evaluating a function allows for more concise, expressive syntax. Now consider a trivial function: Example 4.2. apply :: (a -> b) -> a -> b 8
  • 11. In this example, we have an aptly named function apply which will take a function mapping type a to type b, an object of type a and return an object of type b. Alternatively, this can be thought of as a function that will take a function of a certain type and return a function of the same type. The signature is equivalent to: apply :: (a -> b) -> (a -> b) As the name of the function suggests, one function that satisfies this type sig- nature is the function that simply applies the function to its arguments. apply :: (a -> b) -> a -> b apply f x = f x which can be written more concisely by considering the alternative interpreta- tion of the type signature as: apply :: (a -> b) -> (a -> b) apply f = f or even more concisely by taking advantage of the built in identity function id: apply :: (a -> b) -> (a -> b) apply = id Note that manual currying can be accomplished with the use of lambda functions: apply f = x -> f $ f x 4.2 Comparison of Currying One of Haskell’s prime strengths is the ability to reason about programs [4]. If a partially evaluated function could behave differently than its unevaluated counterpart, this would make reasoning more difficult. In languages where par- tial evaluation is achieved via lambda functions, there is an area where behavior can change, as its left to the programmer to properly pass arguments through. Haskell’s form of currying is very similar to the category theory conception, and to the extent of that similarity, it is able to take advantage of the isomorphism. This ability to reason about how a function behaves without considering the context makes it easier to write bug-free code. 5 Functors Functors are analogous to arrows. In fact, if objects are taken to be categories (small categories, actually), then letting arrows be functors between those cate- gories induces a category (the category Cat) [1]. They give a way to map from one category to another, preserving identity and composition of arrows. 9
  • 12. Formally, functors are mappings from one category to another which preserve composition of arrows. Functors map objects in one category to objects in another, and map arrows in one category to arrows in another such that the composition of arrows in one category is the composition of their mappings in the other [1]. This can be expressed with the following commutative diagrams. A B C f f◦g g =⇒ FA FB FC F f F (f◦g) F g Anywhere that the left diagram appears in the original category, the result of applying the functor to those arguments must have the structure on the right. 5.1 Functors in Haskell Example 5.1. class Functor f where fmap :: (a -> b) -> f a -> f b instance Functor [] where fmap _ [] = [] fmap f (x:xs) = (f x) : fmap f xs Functors in haskell are instances of a parametric type class [5]. A parametric type class can be thought of as a type constructor. It is not a concrete type, but given a concrete type can construct a concrete type. As the above example suggests, a list is a Functor. A list is not a concrete type class, but a list of Num’s is a concrete type class. Functors give a way for a function on a type to be applied to the functor of that type.So, for example, any function that can be applied to an element of a list can be applied to a list of elements using fmap. Example 5.2. ghci > fmap (* 2) [(+ 3 1),(+ 3 2),(+ 3 3)] [8 ,10 ,12] ghci > fmap (* 2) $ fmap (+ 3) [1, 2, 3] [8 ,10 ,12] In general, if (1) F is a functor, (2) f, g have type signature a -> b, and b -> c, respectively, and (3) x is of type F a then the following must hold: fmap g.f x = fmap g $ fmap f x 10
  • 13. 5.2 Comparison of Functors Haskell functors, being parametric types, construct concrete types from other concrete types. In this way, they map between Haskell types (making them an endofunctor on the category Hask) [7]. List, [] takes an Int to create [Int] or a Bool to create [Bool]. fmap maps functions between initial categories to functions between the resultant categories. As well, fmap preserves function composition. Haskell’s implementation of functors is completely analogous to the category theoretic concept. 6 Monoids Monoids are sets, X with an associative binary operator, ⊕, such that for any two elements, x, y ∈ X, x ⊕ y ∈ X. A further conditon is that there is an identity element e ∈ X, such that e ⊕ x = x = x ⊕ e ∀x ∈ X [2]. A monoid is also a one object category. The arrows of this category corre- spond to the elements of the monoid, composition in the category is the binary operation in the monoid and the identity arrow in the category is the identity element in the monoid [1]. 6.1 Monoids in Haskell Monoids in Haskell, like Functors, are typeclasses, though they need not be parametric. For example, the set of integers along with multiplication is a monoid. So is the set of integers along with addition. Example 6.1. class Monoid m where mempty :: m mappend :: m -> m -> m Monoids are expected to satisfy the following: mappend mempty x = x mappend x mempty = x mappend x (mappend y z) = mappend (mappend x y) z Defining the set of integers with addition as a monoid can be done as: newtype Sum a = Sum a instance Num a => Monoid (Sum a) where mempty = Sum 0 mappend Sum x Sum y = Sum (x + y) So, the identity element is defined to be 0, and appending elements is defined as addition. Defining the set of integers with multiplication as a monoid can be done as: 11
  • 14. newtype Product a = Product a instance Num a => Monoid (Product a) where mempty = Product 1 mappend Product x Product y = Product (x * y) In this case the identity element is 1, while the append operation is multiplica- tion. 6.2 Comparison of Monoids Again, the structure of the Haskell monoid very closely mirrors the structure of the category theory monoid. An identity element is defined, and a binary oper- ation is defined for the typeclass that constructs new elements of the typeclass. The concepts are completely analagous. An understanding of one is equivalent to an understanding of the other. 7 Monads Monads (also known as triples) can be defined in different (equivalent) ways. The most instructive definition, for understanding their relationship with Haskell monads, is the following: Definition 7.1. A Triple (T, η, µ) consists of a functor T, and two natural transformations, η and µ such that the following diagrams commute: T T2 T T3 T2 T T2 T ηT idT µ T η idT T µ µT µ µ (3) [1] where a natural transformation is defined: Definition 7.2. A natural transformation between two functors F and G on a category is a family of arrows {φx}x∈I, indexed by the objects of the category such that for any arrow f in the category, the following diagram commutes: a Fa Ga b Fb Gb f φa F f Gf φb (4) [1] 12
  • 15. 7.1 Monads in Haskell The monad typeclass in Haskell is defined as [7]: Example 7.1. class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b x >>= f = join (fmap f x) join :: m (m a) -> m a join x = x >>= id Thus, a monad in Haskell is a parametric typeclass (like a functor), which provides three methods: a method for taking the parameter of type X to a Monad X, a method for taking a Monad X and a function that maps from X to Monad Y and applying it to the Monad X, and a method for taking a Monad Monad X and reducing it to a Monad X. It is expected that anything being declared as a Monad is already declared as a functor, and will, in fact, be a requirement in a future version of the language standard (originally, Haskell had Monads but not Functors, hence the confusion [4]). It should be noted that definitions have been provided for two of the methods, but they are mutually recursive. Thus, it is sufficient for a programmer instantiating a monad to provide an implementation for either (>>=) or join to also implement the other. A couple of examples may be instructive: 7.1.1 Maybe Example 7.2. data Maybe t = Just t Nothing instance Monad Maybe where return x = Just x join Nothing = Nothing join Just Nothing = Nothing join Just (Just x) = Just x The maybe monad is used to allow for error conditions to propagate through Haskell code. A Maybe X will be either a Just X or Nothing. So a Maybe Num will be either Just x, where x is a Num, or Nothing. Functions applied with fmap (since Monads are also Functors) will return Nothing if the argument was nothing, otherwise Just y where y is the result of applying the function to the argument. So, a series of functions can be applied with fmap, each of which will automatically propagate Nothing values to the top, or just proceed with their execution [5]. 13
  • 16. 7.1.2 IO The IO monad is used to allow a function to communicate with the external world, either by writing to files and devices, or by reading from them. A simple “Hello World” program can be written as: Example 7.3. main = putStrLn "Hello World!" where the type of putStrLn is ghci > :t putStrLn putStrLn :: String -> IO () So, putStrLn is a function that takes a String and returns a IO (). The real power of Monads, however, is in their ability to chain together objects. main = putStrLn "What ’s your name?" >> getLine >>= a -> putStr "Hello , " >> putStrLn a The above program will ask for the user’s name, read in the input and then greet the user. While this is already becoming reminiscent of imperative program- ming, Haskell introduces some syntactic sugar which drives the point home. The above program is equivalent to the following: main = do putStrLn "What ’s your name?" a <- getLine putStr "Hello , " putStrLn a The above syntax makes complex series of operations very intuitive without sacrificing power or expressivity [7]. 7.2 Comparison of Monads While (>>=) is more commonly focused on in the definition of a monad, it is actually join which will provide the more obvious connection to category theory. Since a Monad must already be a functor, one piece of the mapping between Haskell’s Monad and the category theory triple is obvious. The Functor in the triple is the Monad instance itself! The second two mappings are less obvious, however. return is actually equivalent to η and join is equivalent to µ. This can be seen by considering the following. Haskell expects the following rules to hold for any instance of a Monad: join . fmap join == join . join join . fmap return == join . return == id join . fmap (fmap f) == fmap f . join 14
  • 17. The first rule can be seen to be equivalent to part of the commutative diagram defining Monads: T3 T2 T2 T T µ µT µ µ The second rule is equivalent to the other part of the diagram: T T2 T T ηT idT µ T η idT The third rule simply states that for any arrow f : Ta → T2 b, the following diagram commutes: T2 a Ta T3 b T2 b µa T f f µT b which follows from µ being a natural transformation. Thus, Haskell’s definition of monad can be seen to be equivalent to category theory’s definition of Monad. 8 Conclusion Primarily, Haskell has benefited from category theory through its use of currying as a primitive operation and the inclusion of certain analogous concepts, namely functors, monoids and monads. Its history reflects their relative importance, as the Monad typeclass was added before Functors or Monoids were added to the language, and it also provides the most concrete benefit to Haskell. Things such as state, io and error conditions were made possible through the use of Monads, while maintaining a lot of the original benefits of the language’s purity. A future direction for research is to make explicit any results of category theory that are being used to Haskell’s benefit, or any results which can be applied to the Hask category to gain insight. 15
  • 18. References [1] Michael A. Arbib. Arrows, Structures and Functors. Academic Press, 1975. [2] Michael Barr and Charles Wells. Category Theory for Computing Science. Les Publications CRM, 1999. [3] Michael Barr and Charles Wells. Toposes, Triples and Theories. 2005. url: http://www.tac.mta.ca/tac/reprints/articles/12/tr12.pdf. [4] Paul Hudak et al. A History of Haskell: being lazy with class. 2007. url: http://research.microsoft.com/en-us/um/people/simonpj/papers/ history-of-haskell/. [5] Miran Lipova˘ca. Learn You a Haskell for Great Good. url: http : / / learnyouahaskell.com. [6] Bryan O’Sullivan, Don Stewart, and John Goerzen. Real World Haskell. url: http://book.realworldhaskell.org. [7] The Haskell Programming Language. url: https://wiki.haskell.org/ Haskell. 16