Learn Haskell Fast and Hard

Blow your mind with Haskell

Magritte pleasure principle


./00_preamble.lhs

Bend his mind to Haskell can be hard. It was for me. In this article I try to provide what I lacked when I started to learn Haskell.

As Boromir would say: “One does not simply learn Haskell”. To learn Haskell you’ll need to learn far more than just a new language. Haskell use a lot of concepts I’ve never heard about before. But many will be useful for programming even in other languages.

The article will certainly be hard to follow. This is done on purpose. There is no shortcut to learn Haskell. It is hard and challenging. But I believe it is a good thing.

This article can be seen as a very dense introduction to Haskell. The conventional way to learn Haskell is to read two books. First “Learn You a Haskell” and just after “Real World Haskell”. I also believe this is the right way to go. But Haskell is very hard to learn by skimming these books. You’ll have to read them in detail. This is why I believe such an article while difficult to read can be a very good introduction. Furthermore, I believe I missed such an article while learning Haskell.

This actual (long) article contains four parts:

  • Introduction: a fast short example to show Haskell can be friendly.
  • Basic Haskell: Haskell syntax, and some essential notions.
  • Hard Difficulty Part:
    • Functional style; an example from imperative to functional style
    • Types; types and a standard binary tree example
    • Infinite Structure; manipulate an infinite binary tree!
  • Hell Difficulty Part:
    • Deal with IO; A very minimal example
    • IO trick explained; the hidden detail I lacked to understand IO
    • Monads; incredible how we can generalize
    • More on infinite tree; a discussion on infinite tree manipulation.

Note: Each time you’ll see a separator with a filename ending in .lhs, you could click the filename to get this file. If you save the file as filename.lhs, you can run it with

runhaskell filename.lhs

Some might not work, but most will. You should see a link just below.

./00_preamble.lhs


01_basic/10_Introduction/00_hello_world.lhs

Introduction

Install

Haskell logo

Tools:

  • ghc: Compiler similar to gcc for C.
  • ghci: Interactive Haskell (REPL)
  • runhaskell: Execute a program without compiling it. Convenient but very slow compared to compiled program.

Don't be afraid

The Scream

Many book/articles about Haskell start by introducing some esoteric formula (quicksort, fibonacci serie, etc…). I will make the exact opposite. At first I won’t show you any Haskell super power. I will start with similarities between Haskell and other programming languages. Let’s jump in the obligatory “Hello World”.

main = print "Hello World!"

To run it, you can save this code in a hello.hs and:

~ runhaskell ./hello.hs
Hello World!

You could also download the literate Haskell source. You should see a link just above the introduction title. Download this file as 00_hello_world.lhs and:

~ runhaskell 00_hello_world.lhs
Hello World!

01_basic/10_Introduction/00_hello_world.lhs


01_basic/10_Introduction/10_hello_you.lhs

Now, a program that ask your name and display Hello <your name>!.

main = do
    print "What is your name?"
    name <- getLine
    print ("Hello " ++ name ++ "!")

First, let us compare with a similar program in other imperative languages:

 # Python
print "What is your name?"
name = raw_input()
print "Hello %s!" % name
 # Ruby
puts "What is your name?"
name = gets.chomp
puts "Hello #{name}!"
// In C
 #include <stdio.h>
int main (int argc, char **argv) {
    char name[666]; // <- An Evil Number!
    // What if my name is more than 665 character long?
    printf("What is your name?\n"); 
    scanf("%s", name);
    printf("Hello %s!\n", name);
    return 0;
}

The structure is the same, but there are some syntax differences. A major part of this tutorial will explain why.

In Haskell, there is a main function and every object has a type. The type of main is IO (). This means, main will cause side effects. IO is a … . Wait! No! I won’t say it now! I am afraid to terrify you. You might run away crying. For now, I won’t talk about what IO really is.

Just remember that Haskell can look a lot like other imperative languages.

01_basic/10_Introduction/10_hello_you.lhs


01_basic/10_Introduction/20_very_basic.lhs

Very basic Haskell

Picasso minimal owl

Before continuing you need to be warned about some essential properties of Haskell.

Functional

Haskell is a functional language. If you come from imperative language, you’ll have to learn a lot of new things. Hopefully many of these new concepts will help you to program even in imperative languages.

Smart Static Typing

Instead of being in your way like in C, C++ or Java, the type system is here to help you.

Purity

Generally your function won’t modify anything of the outside world. This means, it can’t modify the value of a variable, can’t get user input, can’t write on the screen, can’t launch a missile. On the other hand, parallelism will be very easy to achieve. Haskell makes it clear where effects occurs and where you are pure.

Furthermore there is an essential respected law in Haskell:

Applying a function with the same parameter always return the same value.

Lazyness

This is a very uncommon feature. By default, Haskell evaluate something only when it is needed. As consequence, it provides a very elegant way to manipulate infinite structures for example.

A last warning on how you should read Haskell code. For me, it is like reading scientific papers. Some part are very clear, but when you see a formula, just focus and read slower. Also, while learning Haskell, it really doesn’t matter much if you don’t understand syntax details. If you cross a >>=, <$>, <- or any other weird symbol, just ignore them and follows the flow of the code.

Function declaration

You might be used to declare functions like this:

In C:

int f(int x, int y) {
    return x*x + y*y;
}

In javascript:

function f(x,y) {
    return x*x + y*y;
}

in Python:

def f(x,y) =
    return x*x + y*y;

in Ruby:

def f(x,y)
    x*x + y*y
end

In Scheme:

(define (f x y)
    (+ (* x x) (* y y)))

Finaly, the Haskell way is:

f x y = x*x + y*y

Very clean. No parenthesis, no def.

Don’t forget, Haskell use functions and types a lot. It is thus very easy to define them. The syntax was particularly well thought for these objects.

A Type Example

The common usage is to declare the type of your function. This is not mandatory. The compiler is smart enough to discover it for you.

Let’s play a little.

-- We declare the type using ::
f :: Int -> Int -> Int
f x y = x*x + y*y

main = print (f 2 3)
~ runhaskell 20_very_basic.lhs
13

01_basic/10_Introduction/20_very_basic.lhs


01_basic/10_Introduction/21_very_basic.lhs

Now try

f :: Int -> Int -> Int
f x y = x*x + y*y

main = print (f 2.3 4.2)

You get this error:

21_very_basic.lhs:6:23:
    No instance for (Fractional Int)
      arising from the literal `4.2'
    Possible fix: add an instance declaration for (Fractional Int)
    In the second argument of `f', namely `4.2'
    In the first argument of `print', namely `(f 2.3 4.2)'
    In the expression: print (f 2.3 4.2)

The problem: 2.3 isn’t an Int.

01_basic/10_Introduction/21_very_basic.lhs


01_basic/10_Introduction/22_very_basic.lhs

The solution, don’t declare the type for f. Haskell will infere the most general type for us:

f x y = x*x + y*y

main = print (f 2.3 4.2)

It works! Great, we don’t have to declare a new function for each different type. For example, in C, you’ll have to declare a function for int, for float, for long, for double, etc…

But, what type should we declare? To discover the type Haskell as found for us, just launch ghci:


% ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> let f x y = x*x + y*y
Prelude> :type f
f :: Num a => a -> a -> a

Hey? What is this strange type?

Num a => a -> a -> a

First, let’s focus on the right part a -> a -> a. To understand it, just look at a list of progressive examples:

The written type It’s meaning
Int the type Int
Int -> Int the type function from Int to Int.
Float -> Int the type function from Float to Int.
a -> Int the type function from any type to Int.
a -> a the type function from any type a to the same type a.
a -> a -> a the type function of two arguments of any type a to the same type a.

In the type a -> a -> a, the letter a is a type variable. It means f is a function with two argument and both argument and the result have the same type. The type variable a could take many different type value. For example Int, Integer, Float

So instead of having a forced type like in C with declaring the function for int, long, float, double, etc… We declare only one function like in a dynamic typed language.

Generally a can be any type. For example a String, an Int, but also more complex types, like Trees, other functions, etc… But here our type is prefixed with Num a => .

Num is a typeclass. A typeclass can be understood as a set of types. Num contains only types which behave like numbers. More precisely, Num is class containing types who implement a specific list of functions, and in particular (+) and (*).

Typeclass is a very powerful language construction. We can do some incredibly powerful stuff with this. More on this later.

Finally, Num a => a -> a -> a means:

Let a be a type belonging to the Num typeclass. This is a function from type a to (a -> a).

Yes, strange. In fact, in Haskell no function really have two arguments. Instead all functions have only one argument.

More precisely f 3 4 is equivalent to (f 3) 4. Note f 3 is a function:

f :: Num a :: a -> a -> a

g :: Num a :: a -> a
g = f 3

g y ⇔ 3*3 + y*y

Another notation exists for function. The lambda notation permit us to create function without assigning them a name. We call them anonymous function. We could have written:

g = \y -> 3*3 + y*y

The \ is used because it looks like λ and is ASCII.

If you are not used to functional programming your brain should start to heat up. It is time to make some real application.

01_basic/10_Introduction/22_very_basic.lhs


01_basic/10_Introduction/23_very_basic.lhs

But just before that, we should verify the type system works as expected:

f :: Num a => a -> a -> a
f x y = x*x + y*y

main = print (f 3 2.4)

It works, because, 3 is a valid reprensation for both Frational numbers like Float and for Integer. As 2.4 is a Fractional number, 3 is then interpreted as being also a Fractional number.

01_basic/10_Introduction/23_very_basic.lhs


01_basic/10_Introduction/24_very_basic.lhs

If we force our function to work with different type, it will fail:

f :: Num a => a -> a -> a
f x y = x*x + y*y

x :: Int
x = 3
y :: Float
y = 2.4
main = print (f x y) -- won't work because type x ‡ type y

The comiler complains. The two parameter must have the same type.

If you believe it is a bad idea, and the compiler should make the transformation from a type to another for you, you should really watch this great (and funny) video: WAT

01_basic/10_Introduction/24_very_basic.lhs


01_basic/20_Essential_Haskell/00_notations.lhs

Essential Haskell

Kandinsky Gugg

I suggest you to skim this part. Think of it like a reference. Haskell has a lot of features. Many informations are missing here. Get back here if notation feels strange.

I use the symbol to state that two expression are equivalent. It is a meta notation, does not exists in Haskell. I will also use to show what is the return of an expression.

Notations

Arithmetic
3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3)
Logic
True || False ⇒ True
True && False ⇒ False
True == False ⇒ False
True /= False ⇒ True  (/=) is the operator for different
Powers
x^n     for n an integral
x**y    for y any kind of number (Float for example)

Integer have no limit except the capacity of your machine:

4^103   
102844034832575377634685573909834406561420991602098741459288064

Yeah! And also rational numbers FTW! But you need to import the module Data.Ratio:

$ ghci
....
Prelude> :m Data.Ratio
Data.Ratio> (11 % 15) * (5 % 3)
11 % 9
Lists
[]                      ⇔ empty list
[1,2,3]                 ⇔ List of integral
["foo","bar","baz"]     ⇔ List of String
1:[2,3]                 ⇔ [1,2,3], (:) prepend one element
1:2:[]                  ⇔ [1,2]
[1,2] ++ [3,4]          ⇔ [1,2,3,4], (++) concatenate
[1,2,3] ++ ["foo"]      ⇔ ERROR String ≠ Integral
[1..4]                  ⇔ [1,2,3,4]
[1,3..10]               ⇔ [1,3,5,7,9]
[2,3,5,7,11..100]       ⇔ ERROR! I am not so smart!
[10,9..1]               ⇔ [10,9,8,7,6,5,4,3,2,1]
Strings

In Haskell strings are list of Char.

'a' :: Char
"a" :: [Char]
""  ⇔ []
"ab" ⇔ ['a','b'] ⇔  'a':"b" ⇔ 'a':['b'] ⇔ 'a':'b':[]
"abc" ⇔ "ab"++"c"

Remark: In real code you shouldn’t use list of char to represent text. You should mostly use Data.Text instead. If you want to represent stream of ASCII char, you should use Data.ByteString.

Tuples

The type of couple is (a,b). Elements in a tuple can have different type.

-- All these tuple are valid
(2,"foo")
(3,'a',[2,3])
((2,"a"),"c",3)

fst (x,y)       ⇒  x
snd (x,y)       ⇒  y

fst (x,y,z)     ⇒  ERROR: fst :: (a,b) -> a
snd (x,y,z)     ⇒  ERROR: snd :: (a,b) -> b
Deal with parenthesis

To remove some parenthesis you can use two functions: ($) and (.).

-- By default:
f g h x         ⇔  (((f g) h) x)

-- the $ replace parenthesis from the $
-- to the end of the expression 
f g $ h x       ⇔  f g (h x) ⇔ (f g) (h x)
f $ g h x       ⇔  f (g h x) ⇔ f ((g h) x)
f $ g $ h x     ⇔  f (g (h x))

-- (.) the composition function
(f . g) x       ⇔  f (g x)
(f . g . h) x   ⇔  f (g (h x))

01_basic/20_Essential_Haskell/00_notations.lhs


01_basic/20_Essential_Haskell/10a_Functions.lhs

Useful notations for functions

Just a reminder:

x :: Int            ⇔ x is of type Int
x :: a              ⇔ x can be of any type
x :: Num a => a     ⇔ x can be any type a
                      such that a belongs to Num type class 
f :: a -> b         ⇔ f is a function from a to b
f :: a -> b -> c    ⇔ f is a function from a to (b→c)
f :: (a -> b) -> c  ⇔ f is a function from (a→b) to c

Defining the type of a function before its declaration isn’t mandatory. Haskell infers the most general type for you. But it is considered a good practice to do so.

Infix notation

square :: Num a => a -> a  
square x = x^2

Note ^ use infix notation. For each infix operator there its associated prefix notation. You just have to put it inside parathesis.

square' x = (^) x 2

square'' x = (^2) x

We can remove x in the left and right side! It’s called currying.

square''' = (^2)

Note we can declare function with ' in their name. Here:

squaresquare'square''square '''

Tests

An implementation of the absolute function.

abs x :: Num a => a -> a
abs = if x >= 0 then x else -x

Note: the if .. then .. else Haskell notation is more like the ¤?¤:¤ C operator. You cannot forget the else.

Another equivalent version:

abs' x
     | x >= 0 = x
     | otherwise = -x

Notation warning: indentation is important in Haskell. Like in Python, a bad indendation could break your code!

01_basic/20_Essential_Haskell/10a_Functions.lhs


02_Hard_Part/10_Functions.lhs

Hard Part

The hard part could now begins.

Functional style

Biomechanical Landscape by H.R. Giger

In this section, I give a short example of the impressive refactoring ability provided by Haskell. We will choose a problem and resolve it using a standard imperative way. Then I will make the code evolve. The end result will be both more elegant and easier to adapt.

Let’s resolve the following problem:

Given a list of integer, return the sum of its even numbers.

To show differences between functional and imperative approach, I’ll start by providing an imperative solution (in javascript):

function evenSum(list) {
    var result = 0;
    for (var i=0; i< list.length ; i++) {
        if (list[i] % 2 ==0) {
            result += list[i];
        }
    }
    return result;
}

But, in Haskell we don’t have variable, nor for loop. One solution to achieve the same result without loop is to use recursion.

Remark:
Recursion is generally perceived as slow in imperative language. But it is generally not the case in functional programming. Most of the time Haskell will handle recursive function efficiently.

Here is a C version of the recursive function. Note, for simplicity, I assume the int list should end with the first 0 value.

int evenSum(int *list) {
    return accumSum(0,list);
}

int accumSum(int n, int *list) {
    int x;
    int *xs;
    if (*list == NULL) { // if the list is empty
        return n;
    } else {
        x = list[0]; // let x be the first element of the list
        xs = list+1; // let xs be the list without x
        if ( 0 == (x%2) ) { // if x is even
            return accumSum(n+x, xs);
        } else {
            return accumSum(n, xs);
        }
    }
}

Keep this code in mind. We will translate it in Haskell. But before, I need to introduce three simple but useful function we will use:

even :: Integral a => a -> Bool
head :: [a] -> a
tail :: [a] -> [a]

even verify if a number is even.

even :: Integral a => a -> Bool
even 3  ⇒ False
even 2  ⇒ True

head returns the first element of a list:

head :: [a] -> a
head [1,2,3] ⇒ 1
head []      ⇒ ERROR

tail, returns all element except the first of a list:

tail :: [a] -> [a]
tail [1,2,3] ⇒ [2,3]
tail [3]     ⇒ []
tail []      ⇒ ERROR

Remark that for any non empty list l, l ⇔ (head l):(tail l)

02_Hard_Part/10_Functions.lhs


02_Hard_Part/11_Functions.lhs

The first Haskell solution. The function evenSum returns the sum of all even numbers in a list:

-- Version 1
evenSum :: [Integer] -> Integer

evenSum l = accumSum 0 l

accumSum n l = if l == []
                  then n
                  else let x = head l 
                           xs = tail l 
                       in if even x
                              then accumSum (n+x) xs
                              else accumSum n xs

To test a function you can use ghci:

% ghci
GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load 11_Functions.lhs 
[1 of 1] Compiling Main             ( 11_Functions.lhs, interpreted )
Ok, modules loaded: Main.
*Main> evenSum [1..5]
6

Here is an example of execution1:

*Main> evenSum [1..5]
accumSum 0 [1,2,3,4,5]
1 is odd
accumSum 0 [2,3,4,5]
2 is even
accumSum (0+2) [3,4,5]
3 is odd
accumSum (0+2) [4,5]
4 is even
accumSum (0+2+4) [5]
5 is odd
accumSum (0+2+4) []
l == []
0+2+4
0+6
6

Coming from an imperative language all should seems right. In reality many things can be improved. First, we can generalize the type.

evenSum :: Integral a => [a] -> a

02_Hard_Part/11_Functions.lhs


02_Hard_Part/12_Functions.lhs

Next, we can use sub functions using where or let. This way our accumSum function won’t polute the global name space.

-- Version 2
evenSum :: Integral a => [a] -> a

evenSum l = accumSum 0 l
    where accumSum n l = 
            if l == []
                then n
                else let x = head l 
                         xs = tail l 
                     in if even x
                            then accumSum (n+x) xs
                            else accumSum n xs

02_Hard_Part/12_Functions.lhs


02_Hard_Part/13_Functions.lhs

Next, we can use pattern matching.

-- Version 3
evenSum l = accumSum 0 l
    where 
        accumSum n [] = n
        accumSum n (x:xs) = 
             if even x
                then accumSum (n+x) xs
                else accumSum n xs

What is pattern matching? Use value instead of general parameter name.

Instead of saying: foo l = if l == [] then <x> else <y> You simply state:

foo [] =  <x>
foo l  =  <y>

But pattern matching go even further. It is also able to inspect inside datas. We can replace

 foo l =  let x  = head l 
              xs = tail l
          in if even x 
              then foo (n+x) xs
              else foo n xs

by

 foo (x:xs) = if even x 
                  then foo (n+x) xs
                  else foo n xs

This is a very useful feature. It makes our code both tersier and easier to read.

02_Hard_Part/13_Functions.lhs


02_Hard_Part/14_Functions.lhs

We also can currify a bit our definition by removing the l:

-- Version 4
evenSum :: Integral a => [a] -> a

evenSum = accumSum 0
    where 
        accumSum n [] = n
        accumSum n (x:xs) = 
             if even x
                then accumSum (n+x) xs
                else accumSum n xs

02_Hard_Part/14_Functions.lhs


02_Hard_Part/15_Functions.lhs

Higher Level Functions

Escher

To make things even better we should use higher level functions. What are these beast? Higher level functions are functions taking function as parameter.

Here are some examples:

import Data.List (foldl')
filter :: (a -> Bool) -> [a] -> [a]
map :: (a -> b) -> [a] -> [b]
foldl :: (a -> b -> a) -> a -> [b] -> a

Let’s proceed by small steps.

-- Version 5
evenSum l = mysum 0 (filter even l)
    where 
      mysum n [] = n
      mysum n (x:xs) = mysum xs (n+x) 

where

filter even [1..10] ⇔  [2,4,6,8,10]

The function filter takes a function of type (a -> Bool) and a list of type [a]. It returns a list containing only elements for which the function returned true.

Our next step is to use another way to simulate loop. We will use the foldl to accumulate a value. The function foldl capture a general coding pattern:

myfunc list = foo initialValue list
    foo accumulated []     = accumulated
    foo tmpValue    (x:xs) = foo (bar tmpValue x) xs

Which can be replaced by:

myfunc list = foldl bar initialValue list

If you really want to know how the magic works. Here is the definition of foldl.

foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
foldl f z [x1,...xn]
⇔  f (... (f (f z x1) x2) ...) xn

But as Haskell is lazy, it doesn’t evaluate (f z x) and push this to the stack. This is why we generally use foldl' instead of foldl; foldl' is a strict version of foldl. If you don’t understand what lazy and strict means, don’t worry, just follow the code as if foldl and foldl' where identical.

Now our new version of evenSum become:

-- Version 6
-- foldl' isn't accessible by default
-- we need to import it from the module Data.List
import Data.List
evenSum l = foldl' mysum 0 (filter even l)
  where mysum acc value = acc + value

Version we can simplify by using directly a lambda notation. This way we don’t have to create the temporary name mysum.

-- Version 7
-- Generaly it is considered a good practice
-- to import only the necessary function(s)
import Data.List (foldl')
evenSum l = foldl' (\x y -> x+y) (filter even l)

And of course, we remark

(\x y -> x+y) ⇔ (+)

02_Hard_Part/15_Functions.lhs


02_Hard_Part/16_Functions.lhs

Finaly

-- Version 8
import Data.List (foldl')
evenSum :: Integral a => [a] -> a
evenSum l = foldl' (+) 0 (filter even l)

foldl' isn’t the easiest function to intuit. If you are not used to it, you should exercise a bit.

To help you understand what’s going on here, a step by step evaluation:

  evenSum [1,2,3,4]
⇒ foldl' (+) 0 (filter even [1,2,3,4])
⇒ foldl' (+) 0 [2,4]foldl' (+) (0+2) [4]foldl' (+) 2 [4]foldl' (+) (2+4) []foldl' (+) 6 []6

Another useful higher order function is (.). The (.) function correspond to the mathematical composition.

(f . g . h) x ⇔  f ( g (h x))

We can take advantage of this operator to curry a bit more our function:

-- Version 9
import Data.List (foldl')
evenSum :: Integral a => [a] -> a
evenSum = (foldl' (+) 0) . (filter even)

Also, we could rename a bit some part to make it clearer:

-- Version 10 
import Data.List (foldl')
sum' :: (Num a) => [a] -> a
sum' = foldl' (+) 0
evenSum :: Integral a => [a] -> a
evenSum = sum' . (filter even)
 

It is time to discuss a bit. What did we gain by using higher order functions?

At first, you can say it is terseness. But in fact, it has more to do with better thinking. Suppose we want to modify slightly our function. We want to get the sum of all even square of element of the list.

[1,2,3,4] ▷ [1,4,9,16] ▷ [4,16] ▷ 20

Update the version 10 is extremely easy:

squareEvenSum = sum' . (filter even) . (map (^2))
squareEvenSum' = evenSum . (map (^2))
squareEvenSum'' = sum' . (map (^2)) . (filter even)

We just had to add another “transformation function”2.

map (^2) [1,2,3,4] ⇔ [1,4,9,16]

The map function simply apply a function to all element of a list.

We didn’t had to modify inside the function definition. It feels more modular. But there is also you can think more mathematically about your function. You could then use your function as any other one. You could compose, map, fold, filter using your new function.

To modify version 1 is left as an exercise to the reader ☺.

If you believe we reached the end of generalization, then know you are very wrong. For example, there is a way to not only use this function on list but on any recursive type. If you want to know how, I suggest you to read this quite fun article: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, Fokkinga and Paterson.

This example should show you how pure functional programming is great. Unfortunately, using pure functional programming isn’t well suited for all usages. Or at least it isn’t found yet.

One of the great power of Haskell, is the ability to create DSL (Domain Specific Language) making it easy to change the programming paradigm.

In fact, Haskell is also great when you want to write imperative style programming. Understanding this was really hard for me when learning Haskell. A lot of effort is provided to explain you how much functional approach is superior. Then when you attack the imperative style of Haskell, it is hard to understand why and how.

But before talking about this Haskell super-power, we must talk about another essential aspect of Haskell: Types.

02_Hard_Part/16_Functions.lhs


02_Hard_Part/20_Types.lhs

Types

Dali, the madonna of port Lligat

tl;dr:

  • type Name = AnotherType is just an alias and the compiler doesn’t do any difference between Name and AnotherType.
  • data Name = NameConstructor AnotherType make a difference.
  • data can construct structures which can be recursives.
  • deriving is magic and create functions for you.

In Haskell, types are strong and static.

Why is this important? It will help you a lot not to make some mistake. In Haskell, most bugs are catched during the compilation of your program. And the main reason is because of the type inference during compilation. It will be easy to detect where you used the bad parameter at the wrong place for example.

Type inference

Static typing is generally essential to reach fast execution time. But most static typed language are bad to generalize concepts. What saves Haskell is that it can infere types.

Here is a simple example. The square function in Haskell:

square x = x * x

This function can square any Numeral type. You can provide square an Int, an Integer, a Float a Fractional and even Complex. Proof by example:

% ghci
GHCi, version 7.0.4:
...
Prelude> let square x = x*x
Prelude> square 2
4
Prelude> square 2.1
4.41
Prelude> -- load the Data.Complex module
Prelude> :m Data.Complex
Prelude Data.Complex> square (2 :+ 1)
3.0 :+ 4.0

x :+ y is the notation for the complex (x + ib).

Now compare with the necessary C code:

int     int_square(int x) { return x*x; }

float   float_square(float x) {return x*x; }

complex complex_square (complex z) {
    complex tmp; 
    tmp.real = z.real * z.real - z.img * z.img;
    tmp.img = 2 * z.img * z.real;
}

complex x,y;
y = complex_square(x);

For each type, you need to write a new function. The only way to work around this problem is to use some meta-programming trick. For example using the pre-processor. In C++ there is a better way, the C++ templates:

class Number<T> {
    T value;
    square() {
        value = value*value;
    }
}

Number<int> i;
i.square;

Number<float> f;
f.square;

class Complex {
    int real;
    int img;
    Complex operator<*>(Complex z) {
        Complex result;
        result.real = real*z.real - img*z.img;
        result.img  = img*z.real + real*z.img;
        return res;
    }
}

Number<Complex> z;
z.square

Even with C++ templates you are forced to write a line for each type.

To be fair, there is also a definition of the multiplication of Complex in Haskell. But it takes only one line. Somewhere in the source of the module Data.Complex:

instance Num (Complex a) where
  ...
  (x:+y) * (x':+y')   =  (x*x'-y*y') :+ (x*y'+y*x')
  ...

The inference of type gives Haskell a feeling of the freedom that dynamic typed languages provide. But unlike dynamic typed languages, most error are catched before the execution. Generally, in Haskell:

“if it compiles it certainly does what you intended”

02_Hard_Part/20_Types.lhs


02_Hard_Part/21_Types.lhs

Type construction

You can construct your own types. First you can use aliases or type synonyms.

type Name   = String
type Color  = String

showInfos :: Name ->  Color -> String
showInfos name color =  "Name: " ++ name
                        ++ ", Color: " ++ color
name :: Name
name = "Robin"
color :: Color
color = "Blue"
main = putStrLn $ showInfos name color

02_Hard_Part/21_Types.lhs


02_Hard_Part/22_Types.lhs

But it doesn’t protect you much. Try to swap the two parameter of showInfos and run the program:

    putStrLn $ showInfos color name

It will compile and execute. In fact you can replace Name, Color and String everywhere. The compiler will treat them as completely identical.

Another method is to create your own types using the keyword data.

data Name   = NameConstr String
data Color  = ColorConstr String

showInfos :: Name ->  Color -> String
showInfos (NameConstr name) (ColorConstr color) =
      "Name: " ++ name ++ ", Color: " ++ color

name  = NameConstr "Robin"
color = ColorConstr "Blue"
main = putStrLn $ showInfos name color

Now if you switch parameters of showInfos, the compiler complains! A possible mistake you could never do again. The only price is to be more verbose.

Also remark constructor are functions:

NameConstr  :: String -> Name
ColorConstr :: String -> Color

The syntax of data is mainly:

data TypeName =   ConstructorName  [types]
                | ConstructorName2 [types]
                | ...

Generally the usage is to use the same name for the DataTypeName and DataTypeConstructor.

Example:

data Complex = Num a => Complex a a

Also you can use the record syntax:

data DataTypeName = DataConstructor {
                      field1 :: [type of field1]
                    , field2 :: [type of field2]
                    ...
                    , fieldn :: [type of fieldn] }

And many accessor are made for you. Furthermore you can use another order when setting values.

Example:

data Complex = Num a => Complex { real :: a, img :: a}
c = Complex 1.0 2.0
z = Complex { real = 3, img = 4 }
real c ⇒ 1.0
img z ⇒ 4

02_Hard_Part/22_Types.lhs


02_Hard_Part/23_Types.lhs

Recursive type

You already encountered recursive types: lists. You can re-create lists, but with a more verbose syntax:

data List a = Empty | Cons a (List a)

If you really want to use an easier syntax you can use infix name for constructors.

infixr 5 :::
data List a = Nil | a ::: (List a)

The number after infixr is the priority.

If you want to be able to print (Show), read (Read), test equality (Eq) and compare (Ord) your new data structure you can tell Haskell to derive the appropriate function for you.

infixr 5 :::
data List a = Nil | a ::: (List a) 
              deriving (Show,Read,Eq,Ord)

When you add deriving (Show) to your data declaration, Haskell create a show function for you. We’ll see soon how you could use your own show function.

convertList [] = Nil
convertList (x:xs) = x ::: convertList xs
main = do
      print (0 ::: 1 ::: Nil)
      print (convertList [0,1])

This print:

0 ::: (1 ::: Nil)
0 ::: (1 ::: Nil)

02_Hard_Part/23_Types.lhs


02_Hard_Part/30_Trees.lhs

Trees

Magritte, l'Arbre

We’ll just give another standard example: binary trees.

import Data.List

data BinTree a = Empty 
                 | Node a (BinTree a) (BinTree a) 
                              deriving (Show)

Also we create a function which transform a list into an ordered binary tree.

treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
                             (treeFromList (filter (>x) xs))

Look at how elegant this function is. In plain English:

  • an empty list will be converted to an empty tree.
  • a list (x:xs) will be converted to the tree where:
    • The root is x
    • Its left subtree is the tree created from the list of the remaining element of xs which are strictly inferior to x and
    • the right subtree is the tree created from the elements strictly superior to x of the list xs.
main = print $ treeFromList [7,2,4,8]

You should obtain the following:

Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)

This is an informative but quite unpleasant representation of our tree.

02_Hard_Part/30_Trees.lhs


02_Hard_Part/31_Trees.lhs

Just for fun, let’s code a better display for our trees. I simply had fun into making a nice function to display tree in a general way. You can safely pass this part if you find it too difficult to follow.

We have few changes to make. We remove the deriving (Show) in the declaration of our BinTree type. And it also might be useful to make our BinTree an instance of (Eq and Ord). We will be able to test equality and compare trees.

data BinTree a = Empty 
                 | Node a (BinTree a) (BinTree a) 
                  deriving (Eq,Ord)

Without the deriving (Show), Haskell doesn’t create a show method for us. We will create our own version of show. To achieve this, we must declare that our newly created type BinTree a is an instance of the type class Show. The general syntax is:

instance Show (BinTree a) where
   show t = ... -- You declare your function here

Here is my version on how to show a binary tree. Don’t worry about the apparent complexity. I made a lot of improvement in order to display even strange objects.

-- declare BinTree a to be an instance of Show
instance (Show a) => Show (BinTree a) where
  -- will start by a '<' before the root
  -- and put a : a begining of line
  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
    where
    -- treeshow pref Tree 
    --   show a tree and start each line with pref
    -- We don't display Empty tree
    treeshow pref Empty = ""
    -- Leaf
    treeshow pref (Node x Empty Empty) = 
                  (pshow pref x)

    -- Right branch is empty
    treeshow pref (Node x left Empty) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " left)

    -- Left branch is empty
    treeshow pref (Node x Empty right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    -- Tree with left and right sons non empty
    treeshow pref (Node x left right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "|--" "|  " left) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    -- show a tree using some prefixes to make it nice
    showSon pref before next t = 
                  pref ++ before ++ treeshow (pref ++ next) t

    -- pshow replace "\n" by "\n"++pref
    pshow pref x = replace '\n' ("\n"++pref) (show x)

    -- replace on char by another string
    replace c new string =
      concatMap (change c new) string
      where
          change c new x 
              | x == c = new
              | otherwise = x:[] -- "x"

The treeFromList method remain identical.

treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
                             (treeFromList (filter (>x) xs))

And now, we can play:

main = do
  putStrLn "Int binary tree:"
  print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
Int binary tree:
< 7
: |--2
: |  |--1
: |  `--4
: |     |--3
: |     `--6
: `--8
:    `--21
:       |--12
:       `--23

Now it is far better! The root is shown by starting by the < character. And each other line start by a :. But we could also use another type.

  putStrLn "\nString binary tree:"
  print $ treeFromList ["foo","bar","baz","gor","yog"]
String binary tree:
< "foo"
: |--"bar"
: |  `--"baz"
: `--"gor"
:    `--"yog"

As we can test equality and order trees, we can make tree of trees!

  putStrLn "\nBinary tree of Char binary trees:"
  print ( treeFromList 
           (map treeFromList ["baz","zara","bar"]))
Binary tree of Char binary trees:
< < 'b'
: : |--'a'
: : `--'z'
: |--< 'b'
: |  : |--'a'
: |  : `--'r'
: `--< 'z'
:    : `--'a'
:    :    `--'r'

This is why I chosen to prefix each line of tree display by : (except for the root).

Yo Dawg Tree

  putStrLn "\nTree of Binary trees of Char binary trees:"
  print $ (treeFromList . map (treeFromList . map treeFromList))
             [ ["YO","DAWG"]
             , ["I","HEARD"]
             , ["I","HEARD"]
             , ["YOU","LIKE","TREES"] ]

Which is equivalent to

print ( treeFromList (
          map treeFromList 
             [ map treeFromList ["YO","DAWG"]
             , map treeFromList ["I","HEARD"]
             , map treeFromList ["I","HEARD"]
             , map treeFromList ["YOU","LIKE","TREES"] ]))

and gives:

Binary tree of Binary trees of Char binary trees:
< < < 'Y'
: : : `--'O'
: : `--< 'D'
: :    : |--'A'
: :    : `--'W'
: :    :    `--'G'
: |--< < 'I'
: |  : `--< 'H'
: |  :    : |--'E'
: |  :    : |  `--'A'
: |  :    : |     `--'D'
: |  :    : `--'R'
: `--< < 'Y'
:    : : `--'O'
:    : :    `--'U'
:    : `--< 'L'
:    :    : `--'I'
:    :    :    |--'E'
:    :    :    `--'K'
:    :    `--< 'T'
:    :       : `--'R'
:    :       :    |--'E'
:    :       :    `--'S'

Remark how duplicate trees aren’t inserted; there is only one tree corresponding to "I","HEARD". We have this for (almost) free, because we have declared Tree to be an instance of Eq.

See how awesome this structure is. We can make tree containing not only integer, string and char, but also other trees. And we can even make a tree containing a tree of trees!

02_Hard_Part/31_Trees.lhs


02_Hard_Part/40_Infinites_Structures.lhs

Infinite Structures

Escher

It is often stated that Haskell is lazy.

In fact, if you are a bit pedantic, you should state that Haskell is non-strict. Lazyness is just a common implementation for non-strict languages.

Then what does not-strict means? From the Haskell wiki:

Reduction (the mathematical term for evaluation) proceeds from the outside in.

so if you have (a+(b*c)) then you first reduce + first, then you reduce the inner (b*c)

For example in Haskell you can do:

-- numbers = [1,2,..]
numbers :: [Integer]
numbers = 0:map (1+) numbers

take' n [] = []
take' 0 l = []
take' n (x:xs) = x:take' (n-1) xs

main = print $ take' 10 numbers

And it stops.

How?

Instead of trying to evaluate numbers entirely, it evaluates elements only when needed.

Also, note in Haskell there is a notation for infinite lists

[1..]   ⇔ [1,2,3,4...]
[1,3..] ⇔ [1,3,5,7,9,11...]

And most function will work with them. Also there exists the function take equivalent to our take'.

02_Hard_Part/40_Infinites_Structures.lhs


02_Hard_Part/41_Infinites_Structures.lhs

This code is mostly the same as the preceeding one.
import Debug.Trace (trace)
import Data.List
data BinTree a = Empty 
                 | Node a (BinTree a) (BinTree a) 
                  deriving (Eq,Ord)
-- declare BinTree a to be an instance of Show
instance (Show a) => Show (BinTree a) where
  -- will start by a '<' before the root
  -- and put a : a begining of line
  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
    where
    treeshow pref Empty = ""
    treeshow pref (Node x Empty Empty) = 
                  (pshow pref x)

    treeshow pref (Node x left Empty) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " left)

    treeshow pref (Node x Empty right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    treeshow pref (Node x left right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "|--" "|  " left) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    -- show a tree using some prefixes to make it nice
    showSon pref before next t = 
                  pref ++ before ++ treeshow (pref ++ next) t

    -- pshow replace "\n" by "\n"++pref
    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)

    -- replace on char by another string
    replace c new string =
      concatMap (change c new) string
      where
          change c new x 
              | x == c = new
              | otherwise = x:[] -- "x"

Suppose we don’t mind having an ordered binary tree. Here is an infinite binary tree:

nullTree = Node 0 nullTree nullTree

A complete binary tree were each node is equal to 0. Now I will prove you can manipulate this object using the following function:

-- take all element of a BinTree 
-- up to some depth
treeTakeDepth _ Empty = Empty
treeTakeDepth 0 _     = Empty
treeTakeDepth n (Node x left right) = let
          nl = treeTakeDepth (n-1) left
          nr = treeTakeDepth (n-1) right
          in
              Node x nl nr

See what occurs for this program:

main = print $ treeTakeDepth 4 nullTree

This code compile, run and stop giving the following result:

<  0
: |-- 0
: |  |-- 0
: |  |  |-- 0
: |  |  `-- 0
: |  `-- 0
: |     |-- 0
: |     `-- 0
: `-- 0
:    |-- 0
:    |  |-- 0
:    |  `-- 0
:    `-- 0
:       |-- 0
:       `-- 0

Just to heat your neurones a bit more, let’s make a slightly more interresting tree:

iTree = Node 0 (dec iTree) (inc iTree)
        where
           dec (Node x l r) = Node (x-1) (dec l) (dec r) 
           inc (Node x l r) = Node (x+1) (inc l) (inc r) 

Another way to create this tree is to use an higher order function. This function should be similar to map, but should work on BinTree instead of list. Here is such a function:

-- apply a function to each node of Tree
treeMap :: (a -> b) -> BinTree a -> BinTree b
treeMap f Empty = Empty
treeMap f (Node x left right) = Node (f x) 
                                     (treeMap f left) 
                                     (treeMap f right)

Hint: I won’t talk more about this here. If you are interrested of the generalization of map to other data structure, search for functor and fmap.

Our definition is now:

infTreeTwo :: BinTree Int
infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) 
                    (treeMap (\x -> x+1) infTreeTwo) 

Look at the result for

main = print $ treeTakeDepth 4 infTreeTwo
<  0
: |-- -1
: |  |-- -2
: |  |  |-- -3
: |  |  `-- -1
: |  `-- 0
: |     |-- -1
: |     `-- 1
: `-- 1
:    |-- 0
:    |  |-- -1
:    |  `-- 1
:    `-- 2
:       |-- 1
:       `-- 3
main = do
  print $ treeTakeDepth 4 nullTree
  print $ treeTakeDepth 4 infTreeTwo

02_Hard_Part/41_Infinites_Structures.lhs


03_Hell/00_Introduction.lhs

Hell Difficulty Part

Congratulation to get so far! Now, some of the really hardcore stuff could start.

If you are like me, you should get the functional style. You should also understand a bit more the advantages of lazyness by default. But you also don’t really understand were to start to make a real program. And in particular:

  • How do you deal with effects?
  • Why is there a strange imperative-like notation for dealing with IO?

Be prepared, answer might be difficult to get. But they all be very rewarding.

03_Hell/00_Introduction.lhs


03_Hell/01_IO/01_progressive_io_example.lhs

Deal With IO

tl;dr:

A typical function doing IO look a lot like an imperative language:

f :: IO a
f = do
  x <- action1
  action2 x
  y <- action3
  action4 x y
  • To set a value to an object we use <- .
  • The type of each line is IO *; in this example:
    • action1 :: IO b
    • action2 x :: IO ()
    • action3 :: IO c
    • action4 x y :: IO a
    • x :: b, y :: c
  • Few objects have the type IO a, this should help you to choose. In particular you cannot use pure function directly here. To use pure function you could do action2 (purefunction x) for example.

In this section, I will explain how to use IO, not how they work. You’ll see how Haskell separate pure from impure part of the program.

Don’t stop because you’re trying to understand the details of the syntax. Answer will come in the next section.

What to achieve?

Ask a user to enter a list of numbers. Print the sum of the numbers

toList :: String -> [Integer]
toList input = read ("[" ++ input ++ "]")

main = do
  putStrLn "Enter a list of numbers (separated by comma):"
  input <- getLine
  print $ sum (toList input)

It should be straightforward to understand the behavior of this program. Let’s analyze the types in more detail.

putStrLn :: String -> IO ()
getLine  :: IO String
print    :: Show a => a -> IO ()

Or more interrestingly, we remark each expression in the do block has a type of IO a.

main = do
  putStrLn "Enter ... " :: IO ()
  getLine               :: IO String
  print Something       :: IO ()

We should also remark the effect of the <- symbol.

do
 x <- something

If something :: IO a then x :: a.

Another important remark to use IO. All line in a do block must have one of the two forms:

action1             :: IO a
                    -- in this case, generally a = ()

or

value <- action2    -- where
                    -- bar z t :: IO b
                    -- value   :: b

These two kind of line will correspond to two different way of sequencing actions. The meaning of this sentence should be clearer at the end of the next section.

03_Hell/01_IO/01_progressive_io_example.lhs


03_Hell/01_IO/02_progressive_io_example.lhs

Now let’s see how this behave. For example, what occur if the user enter something strange? Let’s try:

% runghc 02_progressive_io_example.lhs
Enter a list of numbers (separated by comma):
foo
Prelude.read: no parse

Argh, an evil error message and a crash! The first evolution will be to answer with a more friendly message.

For this, we must detect, something went wrong. Here is one way to do this. Use the type Maybe. It is a very common type in Haskell.

import Data.Maybe

What is this thing? Maybe is a type which takes one parameter. Its definition is:

data Maybe a = Nothing | Just a

This is a nice way to tell there was an error while trying to create/compute a value. The maybeRead function is a great example of this. This is a function similar to the function read3, but if something goes wrong the returned value is Nothing. If the value is right, it returns Just <the value>. Don’t try to understand too much of this function. I use a lower level function than read; reads.

maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
                  [(x,"")]    -> Just x
                  _           -> Nothing

Now to be a bit more readable, we define a function which goes like this: If the string has the wrong format, it will return Nothing. Otherwise, for example for “1,2,3”, it will return Just [1,2,3].

getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"

We simply have to test the value in our main function.

main = IO ()
main = do
  putStrLn "Enter a list of numbers (separated by comma):"
  input <- getLine
  let maybeList = getListFromString input in
      case maybeList of
          Just l  -> print (sum l)
          Nothing -> error "Bad format. Good Bye."

In case of error, we prompt a nice error message.

Remark the type of each expression in the main’s do block remains of the form IO a. The only strange construction is error. I’ll say error msg will simply take the needed type (here IO ()).

One very important thing to note is the type of all the defined function. There is only one function which contains IO in its type: main. That means main is impure. But main use getListFromString which is pure. It is then clear just by looking at declared types where are pure and impure functions.

Why purity matters? I certainly forget many advantages, but the three main reason are:

  • It is far easier to think about pure code than impure one.
  • Purity protect you from all hard to reproduce bugs due to border effects.
  • You can evaluate pure functions in any order or in parallel without risk.

This is why, you should generally put as most code as possible in pure functions.

03_Hell/01_IO/02_progressive_io_example.lhs


03_Hell/01_IO/03_progressive_io_example.lhs

Our next evolution will be to ask the user again and again util it enters a valid answer.

We keep the first part:

import Data.Maybe

maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
                  [(x,"")]    -> Just x
                  _           -> Nothing
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"

Now, we create a function which will ask the user for an integer list until the input is right.

askUser :: IO [Integer]
askUser = do
  putStrLn "Enter a list of numbers (separated by comma):"
  input <- getLine
  let maybeList = getListFromString input in
      case maybeList of
          Just l  -> return l
          Nothing -> askUser

This function is of type IO [Integer]. Such a type means, that we retrieved a value of type [Integer] through some IO actions. Some people might explain while waving their hands:

«This is an [Integer] inside an IO»

If you want to understand the details behind all of this, you’ll have to read the next section. But sincerly, if you just want to use IO. Just exercise a litlle and remember to think about the type.

Finally our main function is quite simpler:

main :: IO ()
main = do
  list <- askUser
  print $ sum list

We have finished with our introduction to IO. This was quite a fast. Here are the main things to remind:

  • in the do bloc, each expression must have the type IO a. You are then limited in the number of expression you could use. For example, getLine, print, putStrLn, etc…
  • Try to externalize the pure function as much as possible.
  • the IO a type means: an IO action which return an element of type a. IO represent action; under the hood, IO a is the type of a function. Read the next section if you are curious.

If you exercise a bit, you should be able to use IO.

Exercises:

  • Make a program that sum all its argument. Hint: use the function getArgs.

03_Hell/01_IO/03_progressive_io_example.lhs


03_Hell/01_IO/20_Detailled_IO.lhs

IO trick explained

Here is a tl;dr: for this section.

To separate pure from impure part, the main is defined as a function which modify the state of the world

main :: World -> World

A function is granted to have side effect only if it gets this value. But look at a typical main function:

main w0 = 
    let (v1,w1) = action1 w0 in
    let (v2,w2) = action2 v1 w1 in
    let (v3,w3) = action3 v2 w2 in
    action4 v3 w3

We have a lot of temporary elements (here w1, w2 and w3) which must be passed to the next action.

We create a function bind or (>>=). With bind we need no more temporary name.

main =
  action1 >>= action2 >>= action3 >>= action4

Bonus: Haskell has a syntactical sugar for us:

main = do
  v1 <- action1 
  v2 <- action2 v1
  v3 <- action3 v2
  action4 v3

Why did we used some strange syntax, and what exactly is this IO type. It looks a bit like magic.

For now let’s just forget about all the pure part of our program, and focus on the impure part:

askUser :: IO [Integer]
askUser = do
  putStrLn "Enter a list of numbers (separated by commas):"
  input <- getLine
  let maybeList = getListFromString input in
      case maybeList of
          Just l  -> return l
          Nothing -> askUser

main :: IO ()
main = do
  list <- askUser
  print $ sum list

First remark; it looks like an imperative structure. Haskell is powerful enough to make some pure code to look imperative. For example, if you wish you could create a while in Haskell. In fact, for dealing with IO, imperative style is generally more appropriate.

But, you should had remarked the notation is a bit unusual. Here is why, in detail.

In an impure language, the state of the world can be seen as a huge hidden global variable. This hidden variable is accessible by all function of your language. For example, you can read and write a file in any function. The fact a file exists or not, can be seen as different state of the world.

For Haskell this state is not hidden. It is explicitely said main is a function that potentially change the state of the world. It’s type is then something like:

main :: World -> World

Not all function could have access to this variable. Those who have access to this variable can potentienly be impure. Functions whose the world variable isn’t provided to should be pure4.

Haskell consider the state of the world is an input variable for main. But the real type of main is closer to this one5:

main :: World -> ((),World)

The () type is the null type. Nothing to see here.

Now let’s rewrite our main function with this in mind:

main w0 =
    let (list,w1) = askUser w0 in
    let (x,w2) = print (sum list,w1) in
    x 

First, we remark, that all function which have side effect must have the type:

World -> (a,World)

Where a is the type of result. For example, a getChar function should have the type World -> (Char,World).

Another thing to remark is the trick to fix the order of evaluation. In Haskell to evaluate f a b, you generally have many choices:

  • first eval a then b then f a b
  • first eval b then a then f a b.
  • eval a and b in parallel then f a b

This is true, because we should work in a pure language.

Now, if you look at the main function, it is clear you must eval the first line before the second one since, to evaluate the second line you have to get a parameter given by the evaluation of the first line.

Such trick works nicely. The compiler will at each step provide a pointer to a new real world id. Under the hood, print will evaluate as:

  • print something on the screen
  • modify the id of the world
  • evaluate as ((),new world id).

Now, if you look at the style of the main function, it is clearly awkward. Let’s try to make the same to the askUser function:

askUser :: World -> ([Integer],World)

Before:

askUser :: IO [Integer]
askUser = do
  putStrLn "Enter a list of numbers:"
  input <- getLine
  let maybeList = getListFromString input in
      case maybeList of
          Just l  -> return l
          Nothing -> askUser

After:

askUser w0 =
    let (_,w1)     = putStrLn "Enter a list of numbers:" in
    let (input,w2) = getLine w1 in
    let (l,w3)     = case getListFromString input of
                      Just l   -> (l,w2)
                      Nothing  -> askUser w2
    in
        (l,w3)

This is similar, but awkward. Look at all these temporary w? names.

The lesson, is, naive IO implementation in Pure functional language is awkward!

Fortunately, some have found a better way to handle this problem. We see a pattern. Each line is of the form:

let (y,w') = action x w in

Even if for some line the first x argument isn’t needed. The output type is a couple, (answer, newWorldValue). Each function f must have a type of kind:

f :: World -> (a,World)

Not only this, but we can also remark we use them always with the following general pattern:

let (y,w1) = action1 w0 in
let (z,w2) = action2 w1 in
let (t,w3) = action3 w2 in
...

Each action can take 0 to some parameters. And in particular, each action can take a parameter from the result of a line above.

For example, we could also have:

let (_,w1) = action1 x w0   in
let (z,w2) = action2 w1     in
let (_,w3) = action3 x z w2 in
...

And of course actionN w :: (World) -> (a,World).

IMPORTANT, there are only two important pattern for us:

let (x,w1) = action1 w0 in
let (y,w2) - action2 w1 in

and

let (_,w1) = action1 w0 in
let (y,w2) = action2 w1 in

Jocker pencil trick

Now, we will make a magic trick. We will make the temporary world symbol “disappear”. We will bind the two lines. Let’s define the bind function. Its type is quite intimidating at first:

bind :: (World -> (a,World)) 
        -> (a -> (World -> (b,World))) 
        -> (World -> (b,World)) 

But remember that (World -> (a,World)) is the type for an IO action. Now let’s rename it for clarity:

type IO a = World -> (a, World)

Some example of functions:

getLine :: IO String
print :: Show a => a -> IO ()

getLine is an IO action which take a world as parameter and return a couple (String,World). Which can be said as: getLine is of type IO String. Which we also see as, an IO action which will return a String “embeded inside an IO”.

The function print is also interresting. It takes on argument which can be shown. In fact it takes two arguments. The first is the value to print and the other is the state of world. It then return a couple of type ((),World). This means it changes the world state, but don’t give anymore data.

This type help us simplify the type of bind:

bind :: IO a 
        -> (a -> IO b) 
        -> IO b

It says that bind takes two IO actions as parameter and return another IO action.

Now, remember the important patterns. The first was:

let (x,w1) = action1 w0 in
let (y,w2) = action2 x w1 in
(y,w2)

Look at the types:

action1  :: IO a
action2  :: a -> IO b
(y,w2)   :: IO b

Doesn’t seem familiar?

(bind action1 action2) w0 =
    let (x, w1) = action1 w0
        (y, w2) = action2 x w1
    in  (y, w2)

The idea is to hide the World argument with this function. Let’s go: As example imagine if we wanted to simulate:

let (line1,w1) = getLine w0 in
let ((),w2) = print line1 in
((),w2)

Now, using the bind function:

(res,w2) = (bind getLine (\l -> print l)) w0

As print is of type (World → ((),World)), we know res = () (null type). If you didn’t saw what was magic here, let’s try with three lines this time.

let (line1,w1) = getLine w0 in
let (line2,w2) = getLine w1 in
let ((),w3) = print (line1 ++ line2) in
((),w3)

Which is equivalent to:

(res,w3) = bind getLine (\line1 ->
             bind getLine (\line2 -> 
               print (line1 ++ line2)))

Didn’t you remark something? Yes, there isn’t anymore temporary World variable used anywhere! This is MA. GIC.

We can use a better notation. Let’s use (>>=) instead of bind. (>>=) is an infix function like (+); reminder 3 + 4 ⇔ (+) 3 4

(res,w3) = getLine >>=
           \line1 -> getLine >>=
           \line2 -> print (line1 ++ line2)

Ho Ho Ho! Happy Christmas Everyone! Haskell has made a syntactical sugar for us:

do
  y <- action1
  z <- action2
  t <- action3
  ...

Is replaced by:

action1 >>= \x ->
action2 >>= \y ->
action3 >>= \z ->
...

Note you can use x in action2 and x and y in action3.

But what for line not using the <-? Easy another function blindBind:

blindBind :: IO a -> IO b -> IO b
blindBind action1 action2 w0 =
    bind action (\_ -> action2) w0

I didn’t curried this definition for clarity purpose. Of course we can use a better notation, we’ll use the (>>) operator.

And

do
    action1
    action2
    action3

Is transformed into

action1 >>
action2 >> 
action3

Also, another function is quite useful.

putInIO :: a -> IO a
putInIO x = IO (\w -> (x,w))

This is the general way to put pure value inside the “IO context”. The general name for putInIO is return. This is quite a bad name when you learn Haskell. return is very different from what you might be used to.

03_Hell/01_IO/20_Detailled_IO.lhs


03_Hell/01_IO/21_Detailled_IO.lhs

To finish, let’s translate our example:


askUser :: IO [Integer]
askUser = do
  putStrLn "Enter a list of numbers (separated by commas):"
  input <- getLine
  let maybeList = getListFromString input in
      case maybeList of
          Just l  -> return l
          Nothing -> askUser

main :: IO ()
main = do
  list <- askUser
  print $ sum list

Is translated into:

import Data.Maybe

maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
                  [(x,"")]    -> Just x
                  _           -> Nothing
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
askUser :: IO [Integer]
askUser = 
    putStrLn "Enter a list of numbers (sep. by commas):" >>
    getLine >>= \input ->
    let maybeList = getListFromString input in
      case maybeList of
        Just l -> return l
        Nothing -> askUser

main :: IO ()
main = askUser >>=
  \list -> print $ sum list

You can compile this code to verify it continues to work.

Left as an exercise to the masochistic reader:

  • rewrite everything without using (>>), (>>=) and return.

03_Hell/01_IO/21_Detailled_IO.lhs


03_Hell/02_Monads/10_Monads.lhs

Monads

Now the secret can be revealed: IO is a monad. Being a monad means you have access to some syntactical sugar with the do notation. But mainly, you have access to some coding pattern which will ease the flow of your code.

Important remarks:

  • Monad are not necessarily about effects! There are a lot of pure monads.
  • Monad are more about sequencing

For the Haskell language Monad is a typeclass. To be an instance of this typeclass, you must provide the functions (>>=) and return. The function (>>) will be derived from (>>=). Here is how the typeclass Monad is declared (mostly):

class Monad m  where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a

  (>>) :: m a -> m b -> m b
  f >> g = f >>= \_ -> g

  -- You should generally safely ignore this function
  -- which I believe exists for historical reason
  fail :: String -> m a
  fail = error

Remarks:

  • the keyword class is not your friend. A Haskell class is not a class like in object model. A Haskell class has a lot similarities with Java interfaces. A better word should have been typeclass. That means a set of types. For a type to belong to a class, all function of the class must be provided for this type.
  • In this particular example of typeclass, the type m must be a type that take an argument. for example IO a, but also Maybe a, [a], etc…
  • To be a useful monad, your function must obey some rule. If your construction does not obey these rules strange things might happens:

    return a >>= k  ==  k a
    m >>= return  ==  m
    m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h
    

Maybe is a monad

There exists a lot of different type that are instance of Monad. One of the easiest to describe is Maybe. If you have a sequence of Maybe values, you could use monad to manipulate them. It is particularly useful to remove very deep if..then..else.. constructions.

Imagine a complex bank operation. You are elligible to gain about 700€ only if you can afford to follow a list of operation without being negative.

deposit  value account = account + value
withdraw value account = account - value

elligible :: (Num a,Ord a) => a -> Bool
elligible account = 
  let account1 = deposit 100 account in
    if (account1 < 0) 
    then False
    else 
      let account2 = withdraw 200 account1 in
      if (account2 < 0) 
      then False
      else 
        let account3 = deposit 100 account2 in
        if (account3 < 0) 
        then False
        else 
          let account4 = withdraw 300 account3 in
          if (account4 < 0) 
          then False
          else 
            let account5 = deposit 1000 account4 in
            if (account5 < 0) 
            then False
            else
              True

main = do
  print $ elligible 300 -- True
  print $ elligible 299 -- False

03_Hell/02_Monads/10_Monads.lhs


03_Hell/02_Monads/11_Monads.lhs

Now, let’s make it better using Maybe and the fact it is a Monad

deposit :: (Num a) => a -> a -> Maybe a
deposit value account = Just (account + value)

withdraw :: (Num a,Ord a) => a -> a -> Maybe a
withdraw value account = if (account < value) 
                         then Nothing 
                         else Just (account - value)

elligible :: (Num a, Ord a) => a -> Maybe Bool
elligible account = do
  account1 <- deposit 100 account 
  account2 <- withdraw 200 account1 
  account3 <- deposit 100 account2 
  account4 <- withdraw 300 account3 
  account5 <- deposit 1000 account4
  Just True

main = do
  print $ elligible 300 -- Just True
  print $ elligible 299 -- Nothing

03_Hell/02_Monads/11_Monads.lhs


03_Hell/02_Monads/12_Monads.lhs

Not bad, but we can make it even better:

deposit :: (Num a) => a -> a -> Maybe a
deposit value account = Just (account + value)

withdraw :: (Num a,Ord a) => a -> a -> Maybe a
withdraw value account = if (account < value) 
                         then Nothing 
                         else Just (account - value)

elligible :: (Num a, Ord a) => a -> Maybe Bool
elligible account =
  deposit 100 account >>=
  withdraw 200 >>=
  deposit 100  >>=
  withdraw 300 >>=
  deposit 1000 >>
  return True

main = do
  print $ elligible 300 -- Just True
  print $ elligible 299 -- Nothing

We have proved Monad are nice to make our code more elegant. Note this idea of code organization, in particular for Maybe can be used in most imperative language. In fact, this is the kind of construction we make naturally.

An important remark:

The first element in the sequence being evaluated to Nothing will stop the complete evaluation. That means, you don’t execute all lines. You have this for free, thanks to lazyness.

The Maybe monad proved to be useful while being a very simple example. We saw the utility of the IO monad. But now a cooler example, lists.

03_Hell/02_Monads/12_Monads.lhs


03_Hell/02_Monads/13_Monads.lhs

The list monad

Golconde de Magritte

The list monad help us to simulate non deterministic computation. Here we go:

import Control.Monad (guard)

allCases = [1..10]

resolve :: [(Int,Int,Int)]
resolve = do
              x <- allCases
              y <- allCases
              z <- allCases
              guard $ 4*x + 2*y < z
              return (x,y,z)

main = do
  print resolve

MA. GIC. :

[(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)]

For the list monad, there is also a syntactical sugar:

  print $ [ (x,y,z) | x <- allCases, 
                      y <- allCases, 
                      z <- allCases, 
                      4*x + 2*y < z ]

I won’t list all the monads, but there is a lot of monads. The usage of monad simplify the manipulation of some notion in pure languages. In particular, monad are very useful for:

  • IO,
  • non deterministic computation,
  • generating pseudo random numbers,
  • keeping configuration state,
  • writing state,

If you have followed me until here, then you’ve done it! You know monads6!

03_Hell/02_Monads/13_Monads.lhs


03_Hell/03_More_on_infinite_trees/10_Infinite_Trees.lhs

More on Infinite Tree

In the section Infinite Structures we saw some simple construction. Unfortunately we removed two properties of our tree:

  1. no duplicate node value
  2. well ordered tree

In this section we will try to keep the first property. Concerning the second one, we must relax this one but we’ll discuss on how to keep it as much as possible.

This code is mostly the same as the one in the [tree section](#trees).
import Data.List
data BinTree a = Empty 
                 | Node a (BinTree a) (BinTree a) 
                  deriving (Eq,Ord)

-- declare BinTree a to be an instance of Show
instance (Show a) => Show (BinTree a) where
  -- will start by a '<' before the root
  -- and put a : a begining of line
  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
    where
    treeshow pref Empty = ""
    treeshow pref (Node x Empty Empty) = 
                  (pshow pref x)

    treeshow pref (Node x left Empty) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " left)

    treeshow pref (Node x Empty right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    treeshow pref (Node x left right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "|--" "|  " left) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    -- show a tree using some prefixes to make it nice
    showSon pref before next t = 
                  pref ++ before ++ treeshow (pref ++ next) t

    -- pshow replace "\n" by "\n"++pref
    pshow pref x = replace '\n' ("\n"++pref) (show x)

    -- replace on char by another string
    replace c new string =
      concatMap (change c new) string
      where
          change c new x 
              | x == c = new
              | otherwise = x:[] -- "x"

Our first step is to create some pseudo-random number list:

shuffle = map (\x -> (x*3123) `mod` 4331) [1..]

Just as reminder here are the definition of treeFromList

treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList []    = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
                             (treeFromList (filter (>x) xs))

and treeTakeDepth:

treeTakeDepth _ Empty = Empty
treeTakeDepth 0 _     = Empty
treeTakeDepth n (Node x left right) = let
          nl = treeTakeDepth (n-1) left
          nr = treeTakeDepth (n-1) right
          in
              Node x nl nr

See the result of:

main = do
      putStrLn "take 10 shuffle"
      print $ take 10 shuffle
      putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
      print $ treeTakeDepth 4 (treeFromList shuffle)
% runghc 02_Hard_Part/41_Infinites_Structures.lhs
take 10 shuffle
[3123,1915,707,3830,2622,1414,206,3329,2121,913]
treeTakeDepth 4 (treeFromList shuffle)

< 3123
: |--1915
: |  |--707
: |  |  |--206
: |  |  `--1414
: |  `--2622
: |     |--2121
: |     `--2828
: `--3830
:    |--3329
:    |  |--3240
:    |  `--3535
:    `--4036
:       |--3947
:       `--4242

Yay! It ends! Beware thought, it will only work if you always have something to put into a branch.

For example

treeTakeDepth 4 (treeFromList [1..]) 

will loop forever. Simply because, it will try to access the head of filter (<1) [2..]. But filter is not smart enought to understand that the result is the empty list.

Nonetheless, it is still a very cool example of what non strict program has to offer.

Left as an exercise to the reader:

  • Could you prove that there exists some number n such that treeTakeDepth n (treeFromList shuffle) will enter in an infinite loop.
  • Find an upper bound for n.
  • Prove there is no shuffle list such that, for any depth, the program ends.

03_Hell/03_More_on_infinite_trees/10_Infinite_Trees.lhs


03_Hell/03_More_on_infinite_trees/11_Infinite_Trees.lhs

This code is mostly the same as the preceeding one.
import Debug.Trace (trace)
import Data.List
data BinTree a = Empty 
                 | Node a (BinTree a) (BinTree a) 
                  deriving (Eq,Ord)
-- declare BinTree a to be an instance of Show
instance (Show a) => Show (BinTree a) where
  -- will start by a '<' before the root
  -- and put a : a begining of line
  show t = "< " ++ replace '\n' "\n: " (treeshow "" t)
    where
    treeshow pref Empty = ""
    treeshow pref (Node x Empty Empty) = 
                  (pshow pref x)

    treeshow pref (Node x left Empty) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " left)

    treeshow pref (Node x Empty right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    treeshow pref (Node x left right) = 
                  (pshow pref x) ++ "\n" ++
                  (showSon pref "|--" "|  " left) ++ "\n" ++
                  (showSon pref "`--" "   " right)

    -- show a tree using some prefixes to make it nice
    showSon pref before next t = 
                  pref ++ before ++ treeshow (pref ++ next) t

    -- pshow replace "\n" by "\n"++pref
    pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x)

    -- replace on char by another string
    replace c new string =
      concatMap (change c new) string
      where
          change c new x 
              | x == c = new
              | otherwise = x:[] -- "x"

treeTakeDepth _ Empty = Empty
treeTakeDepth 0 _     = Empty
treeTakeDepth n (Node x left right) = let
          nl = treeTakeDepth (n-1) left
          nr = treeTakeDepth (n-1) right
          in
              Node x nl nr

In order to resolve these problem we will modify slightly our treeFromList and shuffle function.

A first problem, is the lack of infinite different number in our implementation of shuffle. We generated only 4331 different numbers. To resolve this we make a slightly better shuffle function.

shuffle = map rand [1..]
          where 
              rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2)
              p x = m*x^2 + n*x + o -- some polynome
              m = 3123    
              n = 31
              o = 7641
              c = 1237

This shuffle function has the property (hopefully) not to have an upper nor lower bound. But having a better shuffle list isn’t enough not to enter an infinite loop.

Generally, we cannot decide whether filter (<x) xs is empty. Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. This new version of code can create binary tree which don’t have the following property for some of its nodes:

Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.

Remark it will remains mostly an ordered binary tree. Furthermore, by construction, each node value is unique in the tree.

Here is our new version of treeFromList. We simply have replaced filter by safefilter.

treeFromList :: (Ord a, Show a) => [a] -> BinTree a
treeFromList []    = Empty
treeFromList (x:xs) = Node x left right
          where 
              left = treeFromList $ safefilter (<x) xs
              right = treeFromList $ safefilter (>x) xs

This new function safefilter is almost equivalent to filter but don’t enter infinite loop if the result is a finite list. If it cannot find an element for which the test is true after 10000 consecutive steps, then it considers to be the end of the search.

safefilter :: (a -> Bool) -> [a] -> [a]
safefilter f l = safefilter' f l nbTry
  where
      nbTry = 10000
      safefilter' _ _ 0 = []
      safefilter' _ [] _ = []
      safefilter' f (x:xs) n = 
                  if f x 
                     then x : safefilter' f xs nbTry 
                     else safefilter' f xs (n-1) 

Now run the program and be happy:

main = do
      putStrLn "take 10 shuffle"
      print $ take 10 shuffle
      putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
      print $ treeTakeDepth 8 (treeFromList $ shuffle)

You should realize the time to print each value is different. This is because Haskell compute each value when it needs it. And in this case, this is when asked to print it on the screen.

Impressively enough, try to replace the depth from 8 to 100. It will work without killing your RAM! The flow and the memory management is done naturally by Haskell.

Left as an exercise to the reader:

  • Even with large constant value for deep and nbTry, it seems to work nicely. But in the worst case, it can be exponential. Create a worst case list to give as parameter to treeFromList.
    hint: think about ([0,-1,-1,....,-1,1,-1,...,-1,1,...]).
  • I first tried to implement safefilter as follow:
    safefilter' f l = if filter f (take 10000 l) == []
                      then []
                      else filter f l
    

    Explain why it doesn’t work and can enter into an infinite loop.

  • Suppose that shuffle is real random list with growing bounds. If you study a bit this structure, you’ll discover that with probability 1, this structure is finite. Using the following code (suppose we could use safefilter' directly as if was not in the where of safefilter) find a definition of f such that with probability 1, treeFromList’ shuffle is infinite. And prove it. Disclamer, this is only a conjecture.
treeFromList' []  n = Empty
treeFromList' (x:xs) n = Node x left right
    where
        left = treeFromList' (safefilter' (<x) xs (f n)
        right = treeFromList' (safefilter' (>x) xs (f n)
        f = ???

03_Hell/03_More_on_infinite_trees/11_Infinite_Trees.lhs


  1. I know I cheat. But I will talk about non-strict later.

  2. You should remark squareEvenSum'' is more efficient that the two other versions. The order of (.) is important.

  3. Which itself is very similar to the javascript eval on a string containing JSON).

  4. There are some unsafe exception to this rule. But you shouldn’t see such usage on a real application except might be for some debugging purpose.

  5. For the curious the real type is data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}. All the # as to do with optimisation and I swapped the fields in my example. But mostly, the idea is exactly the same.

  6. Well, you’ll certainly need to exercise a bit to be used to them and to understand when you can use them and create your own. But you already made a big step further.

comments

Follow me
Copyright ©, Yann Esposito
Created: 02/08/2012 Modified: 02/29/2012
Entirely done with Vim and nanoc