Alt .Net Haskell Workshop 2017
An workshop intended to introduce Haskell to the Alt .Net community and friends!
If you wish to attend on the day, then please register via Eventbrite.
Press "t" to toggle showing the table of contents

Outcomes include...
- Creating, editing, running and interacting with Haskell programs
- Building the confidence to solve problems in the wild with Haskell
- Developing an understanding of the Haskell ecosystem
- Interacting with others in a collaborative environment
If you are attending the workshop, make sure that you RSVP via Eventbrite. Please also attempt to have the required items from the 'Resources' section available for your use during the workshop.
If you would like to volunteer to help assist at the workshop, please send an email to the .
Table of Contents
Resources | Resources Available and Required | 1m |
Welcome | Motivation, Overview, and Approach | 15m |
Setup | Setting up your Haskell environment | 15m |
Ecosystem | Resources and Community | 30m |
Introduction | Introductory Exercises | 30m |
~ Lunch Break ~ | Pizza -> IO Nomnomnomnomnom | 1h |
Types | The Haskell Type System | 30m |
ADTs | Modelling with data in Haskell | 1h |
Type-Classes | Polymorphism, FP style | 30m |
Monads | IO Monad, Do-Notation | 1h |
Guessing-Game | Let's Make a Guessing Game | 1h |
Web-Site | Making a Web-Site with Scotty | 30m |
Thanks | Thanks and Goodbye | 5m |
Required Resources
Before you begin you will require the following...
A Text-Editor
We are assuming previous programming experience, however, if you would like a recommendation, have a look at Atom, Visual Studio Code, Emacs or Vim. Just make sure that you are fluent enough to embark on exercises as they appear in the workshop.
Stack
In order to run the programs written during this workshop you will need a Haskell installation. The easiest way to get up and running is to install Stack.
A Copy of the Workshop Github Project
The exercises in the project are available in runnable form in the workshop source.
You can grab the source from GitHub:
git clone https://github.com/sordina/alt_dot_net_haskell_workshop
Other Useful Resources
These resources are available to help you with any issues you face when learning Haskell:
#haskell on Freenode
An IRC channel dedicated to discussion of Haskell. This is often the easiest place to fire off a one-off question that is simple enough not to warrant a permanent listing on the internet.
Hackage
Hackage is the primary repository for Haskell packages. It is public, searchable, versioned, and uses Cabal package metadata for classification. Furthermore, this can be used to easily browse package contents, documentation and source-code.
For example, browse the Shake package and look at some of the Modules.
Hoogle
Hoogle is a Haskell module and function search-engine. Hoogle allows you to take advantage of the granular type-system used by Haskell to search not just for function-names, but for function type-signatures.
For example, have a look for the function with signature Text -> ByteString.
/r/haskell
For Reddit users, /r/haskell is a very useful resource with a great deal of information regarding recent developments in the Haskell ecosystem and community. This is a good place to ask some more advanced questions or start a flame-war.
Learn You a Haskell (For Great Good)
Learn You a Haskell (For Great Good) is a wonderful introductory text on Haskell.
Haskell Programming from First Principles
The latest and greatest comprehensive text for learning Haskell.
Programming in Haskell
Another well renowned and modern resource for learning Haskell.
Welcome
Welcome to the Melbourne Alt .Net 2017 Haskell Workshop.
Running on Saturday 25/02/2017 at SEEK.
The intent of this workshop is to provide a working introduction to Haskell for programmers in Melbourne who have not yet used the language in anger.
The workshop is split into chapters. The chapters will start with a few trivial introductory exercises to get your fingers warmed up, then the meat - exercises that should be able to be solved using the material introduced up to that point (with help and hints available if needed).
At the beginning of each chapter will be a small listing of terms, so that you know what you can expect to encounter throughout the text of the chapter. This isn't intended to function as a glossary, but simply give you a heads-up on the scope and let you know what's coming up!
Each chapter will conclude with an open-question. This question should provide inspiration for further consideration of how harder problems could be solved using Haskell, and for more advanced attendees, can be attacked instead of twiddling your thumbs after finishing the main exercise.
Setup
This section will help you get up and running so that you can participate in the workshop and complete the exercises.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Stack | setup | GHCi |
Calculations | $PATH | Loading |
:reload | Redefine | GHC |
Compile | Optimisation | Install |
Pointfree | Ecosystem |
Ensure that you have the following programs installed and functioning correctly:
Stack
Check that you have stack installed:
stack --version
This should output something similar to:
Version 1.1.2 x86_64 hpack-0.14.0
Otherwise, install it!
Linux and OS X:
curl -sSL https://get.haskellstack.org/ | sh
stack setup
stack ghci
Prelude> 1 + 1
"Prelude> " at the start of a line is a prompt.
Prelude refers to the default values and functions available.
As a convention, anything that starts a line with "> " is a
prompt and if you're copying and pasting code you should
exclude the "> " and copy to the right of it!
Windows:
Use the 64-bit installer from https://docs.haskellstack.org/en/stable/install_and_upgrade/#windows.
Provided you use the 64-bit version, you shouldn't need to worry about the PATH issues.
Then run the following in Cmd
:
stack setup
stack ghci
Prelude> 1 + 1
This should output:
2
You can use GHCi to perform calculations other than just "1 + 1".
Here is an example session:
Prelude> 1 + 2 + 3
6
Prelude> 100 / 2
50.0
Prelude> 6 ^ 7
279936
Prelude> ^D
Leaving GHCi.
"^D" refers to holding CONTROL and typing "d".
Using GHCi...
Calculate the price of 42-bakers-dozens of eggs at $3 per-egg.
-- Note that a baker's dozen is 13!
Prelude> 42 * 13 * 3
1638
If ghci is on your PATH, then you can invoke it directly,
however, if you have just installed stack, then you will
need to invoke ghci indirectly by calling
> stack exec -- ghci [ARGS]
Loading files in GHCi
There are many ways to load and execute Haskell code. For the purposes of this workshop, if you do not already have a workflow you are comfortable with, then we suggest the following steps:
- Write and edit your programs in files ending in the extension ".hs"
- When you are ready to test your program, load it into GHCi
- After making modifications to your program, reload the program in GHCi
Say you had written the following program test.hs
:
main = print "hello world"
Load the file in GHCi to see it in action:
> stack exec -- ghci test.hs
GHCi, version 7.6.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
Prelude> main
"hello world"
... Unfortunately there is a bug in the program, so in your editor you make the change required to print "hello, world" with the mandated comma:
main = print "hello, world"
Now, back in GHCi, you can reload the program without exiting the REPL (Read Eval Print Loop):
Prelude> :reload
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
Prelude> main
"hello, world"
Much better!
You can inspect a value (or function) in ghci with the `:info` command
in order to find out a little about its type and definition:
Prelude> :info main
main :: IO () -- Defined at test.hs:1:1
If you just wish to see the type of an expresison, you can use
the `:type` command:
Prelude> :type main
main :: IO ()
* In the previous example, you defined a function 'main'
that printed "hello, world"...
* .. Now, define a new numeric function that prints something else
* Load it in GHCi
* Test your function in GHCi
* Make a modification
* Reload your chages without exiting GHCi
* Test your changes
GHC
Create the following source file (program.hs):
main = print "hello world"
Compile the program as follows:
stack exec -- ghc --make program.hs
Run the program with the following command:
./program
The output should look as follows:
"hello world"
Compiled programs are almost always significantly faster than instructions
run inside GHCi. Even greater speed-ups are possible by using the "-O"
optimisation settings for GHC.
Using GHC...
Compile and run hello-world.
> echo 'main = print "hello friends"' > main.hs
> stack exec -- ghc --make main.hs
[1 of 1] Compiling Main ( main.hs, main.o )
Linking main ...
> ./main
"hello friends"
An open-ended question:
Given that GHC is largely written in Haskell, how was GHC first compiled?
An open-ended question:
What are some of the current issues with the Haskell ecosystem?
Ecosystem
The Haskell ecosystem is large and interesting, it is held together more by convention than by dictation, with the current convention being that open source packages are made available through cabal
on Hackage. On top of this distribution, there is a convenient tool provided by Commercial-Haskell called Stack. Stack builds off the existing ecosystem, but provides stable snapshot releases of compatible packages that makes it easy to install packages that play well together.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Stack | install | pointfree |
ghci | new | Hackage |
Stack
The easiest way for newcomers to get started with Haskell these days is by installing Stack via the steps outlined in the Setup chapter.
Stack provides a plethora of functionality and you can get an inkling of this by invoking stack --help
. However, for the purposes of this workshop you will only really require the use of stack exec --ghci
.
The next steps to take would be the installation of libraries and programs via stack install
and the creation of new stack projects via stack new
.
Install the pointfree package from stack.
Use the `pointfree` command-line program to to check what the
pointfree version of `\x -> \y -> x + y + 1` is.
Did this seem pointless to you?
$ pointfree '\x -> \y -> x + y + 1'
flip flip 1 . ((+) .) . (+)
An open-ended question:
How would you go about publishing your own package to Hackage?
Introduction
The following exercises are intended to be used to warm up your fingers, rather than your brain. These should be run through quickly to get you used to using your development environment.
The majority of your workflow should be performed by writing code in an editor, and testing it in GHCi. You will write the definitions in the editor and test them with simple expressions in GHCi.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Primitives | Prelude | Variables |
Literals | let |
Definitions |
String | Tuples | Functions |
Invocation | Lists | Infix |
Cons | (:) | [] |
Destructuring | Pattern-Matching | head |
Partial | length | map |
Expressiveness |
Primitives
Haskell comes pre-packaged with many primitives available in the Prelude
module that is included by default. You don't need to know them all right now, but you should at least make yourself familiar with the following types, and literal syntax:
Type | What? | Literal Syntax |
---|---|---|
Int | Machine Ints | 42 |
String, [Char] | Strings | "Hello World" |
Bool | Booleans | True, False |
You can type any literal directly into GHCi in order to have it echoed
right back at you. This may be useful for sanity checking that you have
the syntax right!
ghci> 42
42
Variables
In Haskell you can define a variable with the =
sign.
Variables can be defined at the top-level (no-indentation):
myVariable = 2
Variable names should start with a lowercase letter and contain no spaces, or special characters, besides underscores, numbers, and '
.
Some examples of variable names are:
a
my_name
data43'
Define your own variable.
x = "hello"
What is an example of an invalid variable name?
InvalidVariable = 123
String literals look familiar:
myString = "hello world"
Define a variable containing a string.
Tuples
Tuples look like this:
myTuplePair = (1,"hello")
myTupleTrio = (1,"hello",3)
They can be used to group multiple, differently-typed (heterogeneous) values.
Define a variable containing a tuple.
Branching
In order to decide to make a decision in Haskell you can branch in many different ways. Two simple ways are with if
statements, and case
statements.
sunny = True
umbrellasToBring =
if sunny
then 1
else 0
kindOfBagRequired =
case umbrellasToBring
of 0 -> "satchel"
1 -> "backpack"
x -> "duffle"
Another way to perform branching is with pattern-matching
which we will discuss shortly.
It's worth noting that Haskell is white-space sensitive. In general, you can add a new-line and indent in-order to continue an expression, however there are several gotchas. See the indentation page on the wiki for more information.
Functions
Functions are a core part of Haskell. Function definition and invocation look like this:
-- Definition:
myFunction x y ... = ...
-- Invocation:
... myFunction 1 2 ...
This is different to what you might be familiar from a c-familiy language such as Javascript:
// Definition:
function javascriptFunction(a,b ...) { ... }
// Invocation:
javascriptFunction(1,2)
For example:
myAdd x y = x + y
myAdd
takes two numbers and returns the result of the addition of those two numbers.
If you wish to use another function application as an
argument, then you will usually have to enclose it in
a pair of parentheses.
For example:
Prelude> myAdd (myAdd 5 4) 3
12
Define a function `myMultiply` that multiplies 3 numbers.
myMultiply x y z = x * y * z
Use your `myMultiply` function to multiply `4`, `5` and `6734`.
Prelude> myMultiply 4 5 6734
There is also an inline-function lambda-syntax:
\{args...} -> {value}
Here is an example of an "addOne" function:
Prelude> (\x -> x + 1) 2
3
Pattern Matching
One fairly unique feature of ML family languages like Haskell is a dedicated syntax for "pattern-matching". Pattern matching is the practice of deconstructing and dispatching on a value at the time of assignment. Deconstruction and dispatching are orthogonal features, but they are often used together. Deconstruction can take the place of a great deal of "accessor-function" style programming, while dispatching can replace many conditional-branching constructs.
Deconstruction
Drawing on our tuple
example, here is a definition that gets the second element of a tuple:
getSecondElement (e1,e2) = e2
This would be equivalent to:
-- `snd` gets the second element of a tuple.
getSecondElementEquiv t = snd t
Dispatch
Dispatching on a boolean argument instead of using an if statement:
multiplyByFiveIfTrue x True = x * 5
multiplyByFiveIfTrue x False = x
This would be equivalent to:
multiplyByFiveIfTrue x b =
if b then x * 5
else x
Another interesting use-case for dispatch is numbers!
-- http://mathworld.wolfram.com/AckermannFunction.html
ackermann 0 y = y + 1
ackermann x 0 = ackermann (x-1) 1
ackermann x y = ackermann (x-1) (ackermann x (y-1))
Combining Deconstruction and Dispatch
Here is an example combining both deconstruction and dispatch, but taking a tuple argument with a number in the first position and a boolean in the second. The number is used in the definition, and the boolean is used to dispatch:
multiplyByFiveIfTrueTuple (x,True) = x * 5
multiplyByFiveIfTrueTuple (x,False) = x
This would be equivalent to:
multiplyByFiveIfTrueTupleEquiv t =
if (snd t) then (fst t) * 5
else (fst t)
While you can use pattern-matching in function-definitions, you can also use it wherever you bind a value, such as case-statements, where and let blocks, and do-notation. You don't have to use pattern-matching in all cases, and sometimes it's easier to just use an if
statement. That's fine! Pattern-matching simply looks a little cleaner in some cases.
Local Definitions with let
and where
If you wish to create local variable definitions in order to simplify your functions or reduce computation, then you can use let
bindings:
amountOfCatFoodLet =
let
days = 21
servingsPerDay = 2
numberOfCats = 3
numberOfServings = numberOfCats * servingsPerDay * days
servingSize = 100 -- grams
in numberOfServings * servingSize
Or with where
bindings:
amountOfCatFoodWhere = numberOfServings * servingSize
where
days = 21
servingsPerDay = 2
numberOfCats = 3
numberOfServings = numberOfCats * servingsPerDay * days
servingSize = 100 -- grams
Importing Modules
Some functions that you may wish to use aren't part of the Prelude, so you will have to import the modules which they reside in.
This can be done in both your source-code, and GHCi with the import
keyword, although the imports in your source have to be close to the top of the file.
import Data.Char -- Access to all Data.Char functions
import Data.Char as DCH -- Name the import for later use
import qualified Data.Char as D -- Only allow qualified references
import Data.Char (toUpper) -- Only import a set of functions
import Data.Char hiding (isDigit) -- Hide certain functions
Try using Hoogle to search for functions and the modules where they live!
An example of using module imports:
Prelude> import Data.Char (toUpper)
Prelude> reverse (map toUpper "hello")
"OLLEH"
Lists
Lists are a commonly used data-structure in Haskell. Everything in a list has the same type (they are homogeneous).
Lists are built using the infix data-constructor (:)
(pronounced "cons"). They also have a compact notation using [...]
.
List literals look like:
list1 = [1,2,3]
list2 = 1 : 2 : []
list3 = "hello" : "world" : []
More information about why lists can be used the way that they are is contained in the ADTs chapter.
Define a variable containing a list.
myList = [1,2,3]
You can deconstruct a list by pattern matching the head and tail as in the case of this function definition:
f (x:xs) = something something ...
Define a function to get the first element of a list.
myHead (x:xs) = x -- This is a partial function, Beware!
In Prelude
this function is called head
.
"head" is a partial function - It will raise an exception if
called with an empty list.
In Haskell we generally wish to avoid defining partial functions.
Define a variable containing the first element of your list.
Make use of your myHead function in the definition!
myFirstElement = myHead myList
Define Length
Define a function that takes a list and returns the length.
Your solution should have the form of:
myLength [] = ...
myLength (x:xs) = ...
Things to consider:
- What is the length of an empty list? (the base case)
- What is the length of something with an additional element?
- What is the length of xs? Can you use a function for this?
myLength [] = 0
myLength (x:xs) = 1 + myLength xs
Repeated definitions with different argument structures,
such as myLength, is called "pattern-matching". This is
because each line "matches" the "pattern" of its arguments.
Define myMap
Define a function "myMap" that takes a function-argument and a list,
and returns a new list containing the result of the function-argument
applied to each element of the list-argument.
Some concrete examples of such a function may do the following:
- Take a function that divides integers by two, list of ints, and returns a list of doubles.
- Take a function that turns lowercase into uppercase characters, and a String, and returns a string in CAPS. Such an uppercasing function can be found in the
Data.Char
module namedtoUpper
.
Things to consider:
- What is the base-case of myMap?
- What is the inductive-case of myMap?
myMap f [] = []
myMap f (x:xs) = f x : myMap f xs
Combining Lists
Lists can be combined by using the ++
function. This is an infix function similar to +
.
Define a new "betterList" combining your "myList" list with itself
to make it twice as good!
betterList = myList ++ myList
Fun List Functions
For your reading pleasure, here are some definintions of other common functions:
myFilter f [] = []
myFilter f (x:xs) = if f x then x : myFilter f xs
else myFilter f xs
myFold f z [] = z
myFold f z (x:xs) = f x (myFold f z xs)
myReverse [] = []
myReverse (x:xs) = myReverse xs ++ [x]
myElem e [] = False
myElem e (x:xs) = if e == x then True
else myElem e xs
An open-ended question:
What is a good balance between safety and expressiveness in a
programming-language?
Lunch Break
Lunch is provided courtesy of SEEK.

If you're interested in reading some of the source-code for builtin functions while you have lunch then check this out:
https://hackage.haskell.org/package/base-4.9.1.0/docs/src/GHC.List.html#head
Types
Question: How do you create a great program?
Answer: You type it!
In this chapter, we will go over the exercises from the introduction and add types to the examples.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Type | Signature | Inline |
Int | :: | Floating |
Variable | Synonym | String |
Tuple | Function | (,) |
C | Argument | Curried |
Parentheses | Multiply | Lists |
Around-Fix | Prefix-Form | Deconstruction |
head | length | map |
Types
In Haskell, values have associated types. What this means is that any particular value can have its type reasoned about statically. But what is a type? There are primitive types, type-builders, and user-defined types. These make available a parallel language for expressing, reasoning about, and constraining different types. The value-level language and the type-level language are linked in several different ways. One is that through "inference" the types of your expressions have to "make sense". Another way is the explicit type-signatures that you can give you values.
Signatures
Type signatures can be provided inline, or above definitions. Primitive types generally start with a capital letter. The general format goes something like this:
value :: { signature }
value { definition }
In English, you can read this as...
value is of type { signature }...
For example:
x :: Int
x = 3
or
x = (3 :: Int)
It is far more common to place the type-signature above the definition, with inline types only used in situations where ambiguities need to be resolved.
You are defining a floating point variable:
myFloat = 1.1
Give your variable a type-signature.
myFloat :: Float
myFloat = 1.1
Type Synonyms
In Haskell, we can give type-expressions an alias (or synonym) by using the type
keyword. This allows you to cut down the verbosity and chance of errors in your code when you have type expressions that would otherwise be repeated frequently.
An example of this is the String
type-synonym, which is defined as follows:
type String = [Char]
Give your "myString" variable from the previous chapter a type-signature.
myString :: String
myString = "Hello Haskell"
Tuples
Tuple type signatures look the same as the tuples themselves, with types in place of the data.
For example, if you had a tuple of a String and an Int, the type would look as follows:
myTuple :: (String, Int)
myTuple = ("The meaning of life", 42)
Give your previous "myTuplePair" definition a type signature.
Type-Variables
Most primitive-types in a type-signature are expressed with upper-case names. However, if you want to indicate that a type is repeated in a signature, but don't want to lock down what the type is, then you can use a lower-case type-variable.
For example, if we wanted a tuple to have the same type of element in each position, then we could write the following:
... (a,a) ...
This becomes especially important in the context of functions!
Functions
The type signatures of functions in Haskell are a little different from how they look in the more familiar C family languages, but the syntax is very elegant and will allow a higher-level of reasoning than less consistent forms.
The syntax for a function type-signature is of the form:
{functionName} :: {argument} -> {result}
The main idea is that functions in Haskell only ever take one argument. If you wish to define a function that takes more than one argument, then you should, in fact, define a function that takes one argument, then returns another function.
Luckily the syntax for doing this in Haskell looks identical to defining a multi-argument function:
myMultiply x y z = x * y * z
However, the distinction becomes clear with the type-signature:
myMultiply :: Int -> (Int -> (Int -> Int))
myMultiply x y z = x * y * z
If you ask for info of your multiply function before
giving it a type-signature, you will see references to
" Num => "... Ignore that for now, we will cover it in
the type-classes chapter!
Now we can see that the function only takes one argument, then returns a function (that only takes one argument, and returns a function (that only takes one argument, that returns an Int.))
This is known as currying.
Fortunately, Haskell's function syntax is right-associative, allowing us to drop the parentheses:
myMultiply :: Int -> Int -> Int -> Int
myMultiply x y z = x * y * z
... and the syntax for function-application works well with this idea too!
Define a "myMultiplyB" function that multiplies 4 numbers.
Make sure to give your function a type-signature.
myMultiplyB :: Int -> Int -> Int -> Int -> Int
myMultiplyB w x y z = w * x * y * z
Lists
List type-signatures look like:
list1 :: [Int]
list2 :: [Int]
list3 :: [String]
list1 = [1,2,3]
list2 = 1 : 2 : []
list3 = "hello" : "world" : []
list1A :: ([]) Int -- Very rarely used syntax!
list1A = [1]
Comments in Haskell start with "--" for single-line,
and "{- ... -}" for multi-line.
List type signatures are special in that the type-constructor is "Around"-fix. This is not generally possible, and lists are a special case in that regard.
If you find you need to, you can use the list type in prefix-form, as per variable list1A
.
Define a list variable and give it a type-signature.
myList :: [Int]
myList = [1,2,3]
Give your `head` deconstructor function a type-signature.
myHead :: [a] -> a
myHead (x:xs) = x
Length Signature
Give your length function a type-signature.
myLength :: [a] -> Int
myLength [] = 0
myLength (x:xs) = 1 + myLength xs
Map Signature
Give your `map` function a type-signature.
Things to consider:
- What is the type of the first argument of myMap?
- What is the second argument, etc?
- What is the type of the result of myMap?
myMap :: (a -> b) -> [a] -> [b]
myMap f [] = []
myMap f (x:xs) = f x : myMap f xs
Fun List Functions Types
Here are the types for the definintions of the list functions from the previous chapter:
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter f [] = []
myFilter f (x:xs) = if f x then x : myFilter f xs
else myFilter f xs
myFold :: (a -> b -> b) -> b -> [a] -> b
myFold f z [] = z
myFold f z (x:xs) = f x (myFold f z xs)
myReverse :: [a] -> [a]
myReverse [] = []
myReverse (x:xs) = myReverse xs ++ [x]
myElem :: Eq a => a -> [a] -> Bool
myElem e [] = False
myElem e (x:xs) = if e == x then True
else myElem e xs
Try to understand the type-signatures for these functions.
Hint: Try finding a way to say them in English.
An open-ended question:
How many types could a type-checker check...
... if a type checker could check types?
ADTs (Algebraic Data Types)
Algebraic Data Types are THE bread and butter of Haskell programs.
- Functions evaluate data by pattern-matching against ADTs
- Problem-domains are modeled using ADTs
- Laziness is linked to ADTs
- Types can be derived from ADT definitions
But how does that help me?
Lexicon
----------- | ------------- | ------------ |
---|---|---|
ADT | Algebraic | Laziness |
Types | Modelling | Bool |
Enum | C++ | Parameter |
Constructor | Recursive | Concrete |
Kind | * | String |
Int | Maybe | [] |
IO | (->) | Deriving |
data | Show | Type-Class |
List |
Example
An example of an ADT in Haskell:
data MyBool = MyTrue | MyFalse | MyNotSure
should_I_eat_something_bigger_than_my_own_head :: MyBool
should_I_eat_something_bigger_than_my_own_head = MyFalse
With this functionality, you are able to introduce your own "Enum"
values.
The MyBool example is somewhat equivalent to the following C++ code:
enum MyBool { MyTrue, MyFalse, MyNotSure };
With the added bonus of not having out-of-bounds casting ruin your day.
If your problem space can be modeled using various discrete values,
then this form of ADT will allow you to create a program that mirrors
your problem!
You can add parameters to the data constructors:
data MyNullString = Nada | MyString String
stringy :: MyNullString
stringy = MyString "Whatever, It's just a string"
blanky :: MyNullString
blanky = Nada
Constructors can take multiple parameters:
data SomeJunk = Rubbish String | TrashPile String Int Bool
discards :: SomeJunk
discards = TrashPile "Junk Yard" 42 True
Furthermore, ADTs can be recursive:
data MyNumberList = Nada | SomeNumbers Int MyNumberList
numbers :: MyNumberList
numbers = SomeNumbers 1 (SomeNumbers 2 Nada)
Finally, ADTs can be parametrised by other types:
data MyContainer x = Jar x
contained :: MyContainer Int
contained = Jar 1
pun :: MyContainer (MyContainer String)
pun = Jar (Jar "Binks")
In general, the syntax of an ADT looks similar to the following:
ADT := data <TypeName> <Variables> = <Constructors>
TypeName := [A-Z] + [a-z'_]*
Parameters := <ConcreteType> + (" " + <ConcreteType>)*
Constructors := <Constructor> + (" | " + <Constructor>)*
Constructor := <TypeName> + <Parameters>
Variables := <Variable> + (" " + <Variable>)*
Variable := [a-z] + [a-z'_]*
ConcreteType can't be defined syntactically, but it means that your type is "Fully Applied" (in Haskell terms, of kind *
). An example of some concrete types are:
String
Int
Maybe String
[Int]
Examples of some non-concrete types are:
Maybe
IO
(->)
Deriving
One final thing to note is that in order to be able to print values of your data types at the GHCi REPL, you will need your data to be a member of the Show
type-class.
Type-classes are covered in depth in the type-classes chapter.
This is achieved by appending the following text after your data definition:
data MyData = SuperGreat deriving (Show)
Similar classes are needed for ordering and equality. If you get stuck on a missing class, just add the following deriving triple for now:
data MyData = SuperGreat deriving (Eq, Ord, Show)
Exercises
With all of this power at your disposal, it's time to define a list ADT yourself.
Define your own generic list ADT.
Things to consider:
- Should this ADT be parametrised?
- Should this ADT be recursive?
- Should this ADT have multiple constructors?
- Should the constructors be parametrised?
data MyList a = Empty | Items a (MyList a)
An open-ended question:
What would the ADT for a Lisp-like language look like?
If you wish to learn about why ADTs are "Algebraic", then have a look at:
Type Classes
A big part of writing clean reusable code is controlled polymorphism. We are like a carny at the roller-coaster, anybody can ride the roller coaster, but you must be at least this tall.
In the object oriented world we use the inheritance heirachy, we know that if something is a subclass, it has at least all of the features of its parent. Of course this is all intertwined with the sharing of data and state, and that's bad.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Polymorphism | Object-Oriented | Inheritance |
Data | State | Type-Classes |
Functions | Context | => |
Num | (+) | Show |
Read | String | return |
Dispatch | (::) | Definition |
Instance | Declaration |
Functional Type-Classes
In the functional world we get type classes, which is just controlled polymorphism without the baggage. They basically say, that I don't need to know exactly what type you'll call me with but you need to provide a bunch of functions for me to use.
A function tells you what type classes it needs with a "context", The context is the bit to the left of the double arrow "=>"
(+) :: Num a => a -> a -> a
show :: Show a => a -> String
In the above example, we have (+)
. It can work for any type that is a number. There are built in types for numbers and you can also define your own.
show
can turn any "Showable" type into a string. this is analogous to the toString()
method in Java.
Define a function that adds one to everything in a list.
What is the type of this function?
incrementer :: Num a => [a] -> [a]
incrementer l = map (+1) l
Unlike most other languages, with some kind of type driven function dispatch (e.g. methods, overloading). Haskell can also use the return type of the function to choose functionality, this can take a little getting used to, but it is powerful.
read :: Read a => String -> a
incrementAndshow :: (Num a, Show a) => a -> String
read
turns a string into a value of another type, although we don't know what this is yet. It depends, for example on the type of the function that you are passing the result of read into.
incrementAndShow
demonstrates a function that uses two type classes in its context.
Convert a string to an integer using read, then covert
a string into a list of integers using read.
Hints:
* Use (::) to add a type to an expression inline
* If you want to know the format required for your string,
then try showing the value you would like to end up with.
If you just type 'read "1"' in ghci you get an error, why is this?
Define `incrementAndShow` which adds one to a number and
coverts it to a string.
What is they type of this function?
How did haskell infer your context?
Defining Your Own Type Classes
Let's define a type class of things that can be rated, as in 1 to 5 stars or awesome to lame.
data Rating =
SoAwesomeICried |
PrettyCool |
Meh |
ForTheLoveOfGodMakeItEnd
deriving Show
class Rateable r where
rating :: r -> Rating
data Beer = Coopers | Fosters | CarltonDraught
deriving Show
instance Rateable Beer where
rating Coopers = PrettyCool
rating Fosters = ForTheLoveOfGodMakeItEnd
rating CarltonDraught = Meh
data Movie = Movie String
deriving Show
instance Rateable Movie where
rating (Movie "Bladerunner") = SoAwesomeICried
rating (Movie "Tron") = PrettyCool
rating _ = Meh
When we define a type class we write out the name and the types of the functions that we want associated with this type class. We also put a type variable in our definition which refers to whatever type may instance this type class later. We can use this type variable in the definitions below. At this point we can also define default definitions for the functions.
We then define two new data types, Beer
and Movie
for which we add an "instance declaration" this is where we write the definitions of the type class functions specialized for beers of movies.
We now know how to manually add type class definitions and we could add custom Show
instances for each of our new datatypes. However this would be a bunch of boilerplate nonsense, so we can use the handy deriving directive to automatically add a Show
instance for us.
Create your own Rateable type class and wacky Rating datatype.
Then create datatypes and instances for something you have a
strong opinion about, like cars or a political party.
Add a review function to your type class that returns
a short textual review.
Add a Rateable instance for a native type, like String.
There are a few other cool things you can do with typeclasses that are not covered here. So if you want to know more, check out some of these other articles:
Monads
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Monad | Rules | Side-Effects |
Tainted | Function | IO |
Bind | (>>=) | Specialised |
main | getLine | Isomorphic |
Inside | Unwrapped | "and-then" |
return | lift | Vanilla |
Interleaved | do | <- |
DSL | Desugar | AI |
Pure | sequence | forever |
mapM | CQRS | Transform |
Compose | Explicit |
No seriously, what are Monads?
A monad is just a bunch of rules. There are many, many analogies for monads out there. Each of these analogies are useful, but can be obscuring on their own, they are just one view point. To effectively wield monads we must use many different view points together, each one revealing a little piece of the underlying structure of monads. Each view point becomes another tool in our mental toolbox.
So there is no one magic-bullet analogy for monads, only many complementary ones.
Haskell uses monads to represent side-effects. The simplest and most practical analogy is the "tainted value" analogy. In Haskell the function that reads a file looks like this:
readFile :: FilePath -> IO String
This function can't return a normal string, because the return value has been tainted by side effects. So the IO monad is acting as a tag saying that the returned string must be treated specially.
But an IO String
is not very useful to us, because we want to actually do things with it. So Haskell, in its normal paternalistic style, allows us access the String
inside an IO String
only in a very careful way. We use an operation call bind (>>=)
to access the string.
(>>=) :: Monad m => m a -> (a -> m b) -> m b
-- Here specialized for IO and String
(>>=) :: IO String -> (String -> IO b) -> IO b
This says the only way we can "look inside" an IO String
is by providing a function that takes a String
and returns some other new value that has also been tainted by the outside world. In other words we can only look at a value "inside" the IO
monad if we promise to make sure our result will also be tainted.
This means that if some code uses a value from the outside world, even deep inside, it cannot be hidden, it must be made explicit in the type. Tainting is a one way street, once you are tainted you can't be untainted. There is no function untaint :: IO a -> a
. One can't get an untainted value from a tainted one.
In fact, in haskell, the very way we construct a useful program is by ultimately creating value of type IO ()
that we assign to special variable called main
.
Why can't one write untaint?
If you could what problems would this cause?
Aside from "tainting" another way to think about the
"IO" type is as little side-effectful imperative programs.
You can create a new program that sends the output of one
of these programs into the input of another one using
the ">>=" function.
Just like all other values in Haskell, these IO values can
be passed around, duplicated, put in lists, etc. The
interesting thing is that the only way that they can actually
run is by assigning one to a "main" function.
(or calling one from GHCi)...
One thing that can be a little strange is the type of getLine
. In imperative languages, functions of zero arguments make some sense. They can be thought of recipies or to-do lists. In haskell a total function of type () -> Foo
is isomorphic to a constant Foo
. Because the function only has one input value and therefore only one possible output value.
So let us return to getLine
. In an imperative language it would look like getLine :: () -> String
. Our first problem is that the return value of this function is tainted by the outside world, so we need to use the IO
monad, getLine :: () -> IO String
. Now because of the isomorphism between unit domian functions and constants we can just write getLine :: IO String
. We call a constant of type IO a
an "IO action". Because it stands for a bunch of side effects that will be performed together.
This will seem strange at first, because getLine isn't really a function -- it's just a constant value! But that's okay because while the IO String
is a constant (i.e. there is only one distinct IO action that reads a line from the console) the value inside the monad is not constant. It can be different each time we look inside.
> getLine
hello
"hello"
> getLine
monad
"monad"
> getLine >>= (\name -> putStrLn ("Hello " ++ name))
andy
Hello andy
One thing leads to another
Often when doing IO one wants to make sure one thing happens after another, We can use (>>=)
and just ignore the unwrapped value:
putStr "Hello " >>= (\_ -> putStrLn "World")
putStrLn "One" >>= (\_ -> putStrLn "Two") >>= (\_ -> putStrLn "Three")
This pattern can be easily abstracted, it has been standardized as (>>) :: IO a -> IO b -> IO b
. This can be read as "and then".
putStr "Hello " >> putStrLn "World"
putStrLn "One" >> putStrLn "Two" >> putStrLn "Three"
Write a program that prints something stupid, funny or rude.
Make sure to use (>>) somewhere.
Monad Wars III: Return of the Value
We mentioned before that there is no way to untaint a value, once it has been tainted, we can make new tainted values from it, but never untainted ones. But that begs the question, can we choose to taint a value? Indeed we can, in fact, this is a fundamental operation of a Monad. In haskell it is confusingly called return :: a -> IO a
.
In recent versions of GHC (>= 7.10), Monads must also be
Applicative, with the default implementation of `return`
being Applicative's `pure`.
A common pattern is to "open up" an IO
with bind (>>=)
, mess around with the contents then wrap it back up again with return
. We have a function to help us with this called liftM
. Specialized for IO
it has type liftM :: (a -> b) -> (IO a -> IO b)
. We can use this to take a vanilla function and "lift" it into the IO monad. It works by unwrapping the IO monad calling our function and then wrapping the result back up again.
use return to write 'myLiftIO :: (a -> b) -> IO a -> IO b'
Do it, do it good.
Sometimes when you are doing a lot of ad-hoc interleaved IO, using bind and return all over the place can become a pain in the fingers. So haskell provides special syntax for using monads, called "do notation".
To use do notation, we start a "do block" with the keyword do
. Inside a do block, statements can be written line by line and they are automatically joined using "and then" (>>)
. This causes them to be run one after the other like in an imperative programming language. One can also easily unwrap monad values and bind them to a variable with using a left arrow "<-" syntax.
main = do
putStrLn "Hello World"
putStrLn "Enter your name: "
name <- getLine
putStrLn ("Hello: " ++ name)
The do-notation is like a DSL, under the hood it is just expanded to a chain of bind (>>=)
calls. Here is what the above code would look like without do-notation:
main =
putStrLn "Hello World" >>
putStrLn "Enter your name: " >>
getLine >>= (\ name ->
putStrLn ("Hello: " ++ name))
Write a program that asks for someone's first and second name,
then compliments them (or makes fun of them).
For extra points ask for their age, and customize the complement
(or insult) depending on how old they are.
Do it with do-notation first, then "desugar" it into raw bind (>>=) calls
My father once told me about an "amazing AI" that his
magician/programmer friend build, which could answer any yes/no
question as well as a human.
Of course it only worked if his friend was the one typing
the question! The trick being that it just counted the
number of spaces in the question. If there was an even number
it would output true, if there was an odd number, false.
You just fiddled with the wording, or snuck in an extra space,
to get the answer that you wanted...
Looks like it's time to write your super-dooper
human-level AI and see if your friends can figure
out how it works.
Stay Functional, San Diego
Even when we are programming with side effects, we still want our programs to follow functional principles. To stop our code ending up like C written in do-notation, there are some principles we can try to follow:
Try to do most of the work inside pure functions.
Don't create an IO action until the last possible moment.
Be declarative, think of program as declaring a pipeline or specifying an interaction, instead of being a to-do list.
Avoid mixing control, state and calculation. Instead abstract them into small composable pieces. For inspiration see the monad combinators in Control.Monad (e.g.
sequence
,forever
,mapM
).
These principles are not specific to monads. They are applicable to all side-effect heavy programing problems. These principles can also be applied to imperative programs for great justice.
A lot of patterns like reactive programming, dataflow programming and CQRS are consequences of following principles such as these.
The pithy take-away is don't "update, execute, update". Instead "represent, transform, compose".
An open-ended question:
Why is it a good idea to make side effects explicit?
Let's Make a Guessing Game
The Game
Your task is to make a guessing game where the computer comes up with a number, and you have to make guesses - with the computer telling you if you were too high, or too low.
Lexicon
----------- | ------------- | ------------ |
---|---|---|
Game | Stack | main |
System.Random | randomRIO | |
readLn | compare | case |
do | Procedure |
Example Session
$ stack exec -- runhaskell Module_1470214643_41323.hs
"Let's play the number guessing game"
"Enter a number"
2
"Too low :("
"Enter a number"
8
"Too high :("
"Enter a number"
5
"Too high :("
"Enter a number"
4
"You win!"
Solution Toolbox
In order to solve the problem the following should be used:
Tool | Details |
---|---|
main | You must define a main function in your module |
Lets you print things to the console |
|
System.Random | import System.Random to get access randomRIO |
randomRIO | Generates a random number from a range as an IO action |
readLn | read a value from the console as a number |
compare | compare x y tells you if x is LT, EQ, or GT y |
case | Dispatch on a value |
do | Execute multiple IO actions |
Thinking Functionally
Try to build the solution piece-by-piece. Examine how you can use recursion in conjunction with pattern-matching to keep re-running a procedure until a condition is satisfied.
Solution
If you're having trouble coming up with the solution, then here are some hints!
Main
main :: IO ()
main = do
print "Let's play the number guessing game"
n <- randomRIO (1, 10)
game n
Game
game :: Int -> IO ()
game n = do
print "Enter a number"
g <- readLn
case compare g n
of LT -> tooLow n
GT -> tooHigh n
EQ -> print "You win!"
Too Low
tooLow :: Int -> IO ()
tooLow n = do
print "Too low :("
game n
Full Solution
import System.Random
main :: IO ()
main = do
print "Let's play the number guessing game"
n <- randomRIO (1, 10)
game n
game :: Int -> IO ()
game n = do
print "Enter a number"
g <- readLn
case compare g n
of LT -> tooLow n
GT -> tooHigh n
EQ -> print "You win!"
tooLow :: Int -> IO ()
tooLow n = do
print "Too low :("
game n
tooHigh :: Int -> IO ()
tooHigh n = do
print "Too high :("
game n
Making a Web-Site with Scotty
The Haskell library Scotty is similar to the ruby web-library Sinatra.

Beam me Up
Scotty can be installed by using the following Stack command:
> stack install scotty
Scotty's behaviour is based around REST verbs and routes.
For example - A simple Hello-World website:
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Data.Monoid
main = scotty 3000 $ do
get "/:word" $ do
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
The "LANGUAGE" line is a pragma, instructing the compiler to
allow string literals to be of types other than "String".
If we inspect the type of get
in GHCi we see this:
> import Web.Scotty
> :info get
get :: RoutePattern -> ActionM () -> ScottyM ()
The ActionM Monad allows us to perform any IO action we desire, such as printing to the console, reading files, etc - through the use of the liftIO function.
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Control.Monad.IO.Class
import Data.Monoid
myRoute = get "/hello" $ do
liftIO $ putStrLn "about to return hello!"
html "Hi!"
Modify this simple website to show the current time.
Install the "time" package and "import Data.Time.Clock".
Use "show" to convert the time to a String.
Use the pack function from "import Data.Text.Lazy (pack)".
"pack" can convert Strings to Text.
First install the "time" package.
Then define your route as:
import Data.Time.Clock
import Data.Text.Lazy (pack)
myTimeRoute = get "/time" $ do
t <- liftIO getCurrentTime
html $ pack $ show t
-- You can find a full file in the scaffold/website folder.
Modify this simple website to output the answers from the
other chapters in this workshop!
An open question:
What features do the more advanced Haskell web-frameworks include
that Scotty lacks?
Thanks!
We hope you've had a great time today and learned something useful!
We couldn't have put this together without the help of our volunteers, the venue and food from SEEK, and the Alt .Net Meetup group for inviting everyone along too!
Thanks a lot for coming along today and if you've developed a taste for Haskell then please consider coming along to the Melbourne Haskell User's Group Meetup.
MHUG runs monthly at Silverpond a machine-learning consultancy who not-coincidentally also run workshops.