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
Workshop

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 named toUpper.

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.

Chicken Dinner

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?

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:

http://www.haskell.org/tutorial/classes.html

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:

  1. Try to do most of the work inside pure functions.

  2. Don't create an IO action until the last possible moment.

  3. Be declarative, think of program as declaring a pipeline or specifying an interaction, instead of being a to-do list.

  4. 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
print 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
print 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

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.