----- isHidden: false menupriority: 1 kind: article created_at: 2012-02-08T15:17:53+02:00 title: Learn Haskell Fast and Hard subtitle: Blow your mind with Haskell author_name: Yann Esposito author_uri: yannesposito.com tags: - Haskell - programming - functional - tutorial ----- <%= blogimage("magritte_pleasure_principle.jpg","Magritte pleasure principle") %> begindiv(intro) <%= tldr %> A very short and dense tutorial for learning Haskell. >
> runhaskell filename.lhs >> > Some might not work, but most will. > You should see a link just below. enddiv
main = putStrLn "Hello World!"
~ 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
main = do
print "What is your name?"
name <- getLine
print ("Hello " ++ name ++ "!")
# 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
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 be dedicated to explaining 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.
Just remember that Haskell can look a lot like mainstream imperative languages.
01_basic/10_Introduction/10_hello_you.lhs
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)))
Finally, the Haskell way is:
f x y = x*x + y*y
Very clean. No parenthesis, no `def`.
Don't forget, Haskell uses functions and types a lot.
It is thus very easy to define them.
The syntax was particularly well thought for these objects.
-- We declare the type using ::
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2 3)
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2.3 4.2)
f x y = x*x + y*y
main = print (f 2.3 4.2)
% ghciUh? 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 | Its 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 arguments and both arguments 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 dynamically 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 _type class_. A type class 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 `(*)`. Type classes are a very powerful language construct. 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` type class. This is a function from type `a` to (`a -> a`). Yes, strange. In fact, in Haskell no function really has two arguments. Instead all functions have only one argument. But we will note that taking two arguments is equivalent to taking one argument and returning a function taking the second argument as parameter. 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 functions. The lambda notation allows us to create functions 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 a real application. 01_basic/10_Introduction/22_very_basic.lhsGHCi, 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*yPrelude>
:type ff :: Num a => a -> a -> a
f :: Num a => a -> a -> a
f x y = x*x + y*y
main = print (f 3 2.4)
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
square :: Num a => a -> a
square x = x^2
square' x = (^) x 2
square'' x = (^2) x
square''' = (^2)
absolute :: (Ord a, Num a) => a -> a
absolute x = if x >= 0 then x else -x
absolute' x
| x >= 0 = x
| otherwise = -x
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 == 0) { // 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)`
-- 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
% 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] 6Here is an example of execution[^2]: [^2]: I know I cheat. But I will talk about non-strict later.
*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 6Coming 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
-- 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
-- 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
foo [] =
foo l =
But pattern matching go even further.
It is also able to inspect inside data.
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 terser and easier to read.
02_Hard_Part/13_Functions.lhs
f x = (some expresion) x
you can simply write
f = some expression
We use this method to remove 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
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 (n+x) xs
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) xsWhich can be replaced by:
myfunc list = foldl bar initialValue listIf 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
-- Generally it is considered a good practice
-- to import only the necessary function(s)
import Data.List (foldl')
evenSum l = foldl' (\x y -> x+y) 0 (filter even l)
(\x y -> x+y) ⇔ (+)
02_Hard_Part/15_Functions.lhs
-- 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 [] ⇒ 6Another 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 η-reduce 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)
squareEvenSum = sum' . (filter even) . (map (^2))
squareEvenSum' = evenSum . (map (^2))
squareEvenSum'' = sum' . (map (^2)) . (filter even)
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 value;
square() {
value = value*value;
}
}
Number i;
i.square;
Number 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 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 caught before the execution.
Generally, in Haskell:
> "if it compiles it certainly does what you intended"
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
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
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 accessors 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
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)
convertList [] = Nil
convertList (x:xs) = x ::: convertList xs
main = do
print (0 ::: 1 ::: Nil)
print (convertList [0,1])
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Show)
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (x) xs))
main = print $ treeFromList [7,2,4,8]
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord)
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"
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (x) xs))
main = do
putStrLn "Int binary tree:"
print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
putStrLn "\nString binary tree:"
print $ treeFromList ["foo","bar","baz","gor","yog"]
putStrLn "\nBinary tree of Char binary trees:"
print ( treeFromList
(map treeFromList ["baz","zara","bar"]))
putStrLn "\nTree of Binary trees of Char binary trees:"
print $ (treeFromList . map (treeFromList . map treeFromList))
[ ["YO","DAWG"]
, ["I","HEARD"]
, ["I","HEARD"]
, ["YOU","LIKE","TREES"] ]
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
-- 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
nullTree = Node 0 nullTree nullTree
-- 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
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 interesting 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)
-- 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)
infTreeTwo :: BinTree Int
infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
(treeMap (\x -> x+1) infTreeTwo)
main = print $ treeTakeDepth 4 infTreeTwo
~~~
< 0
: |-- -1
: | |-- -2
: | | |-- -3
: | | `-- -1
: | `-- 0
: | |-- -1
: | `-- 1
: `-- 1
: |-- 0
: | |-- -1
: | `-- 1
: `-- 2
: |-- 1
: `-- 3
~~~
02_Hard_Part/41_Infinites_Structures.lhs
toList :: String -> [Integer]
toList input = read ("[" ++ input ++ "]")
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
print $ sum (toList input)
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
import Data.Maybe
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 `read`[^1],
but if something goes wrong the returned value is `Nothing`.
If the value is right, it returns `Just
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
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."
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 = 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
main :: IO ()
main = do
list <- askUser
print $ sum list
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 explicitly 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 potentially be impure.
Functions whose the world variable isn't provided to should be pure[^032001].
[^032001]: 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.
Haskell consider the state of the world is an input variable for `main`.
But the real type of main is closer to this one[^032002]:
[^032002]: 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.
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 similar to:
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
> ~~~
<%= leftblogimage("jocker_pencil_trick.jpg","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
x <- action1
y <- action2
z <- 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 simplified 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.
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
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 type class, 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
> ~~~
deposit value account = account + value
withdraw value account = account - value
eligible :: (Num a,Ord a) => a -> Bool
eligible 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 $ eligible 300 -- True
print $ eligible 299 -- False
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)
eligible :: (Num a, Ord a) => a -> Maybe Bool
eligible 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 $ eligible 300 -- Just True
print $ eligible 299 -- Nothing
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)
eligible :: (Num a, Ord a) => a -> Maybe Bool
eligible account =
deposit 100 account >>=
withdraw 200 >>=
deposit 100 >>=
withdraw 300 >>=
deposit 1000 >>
return True
main = do
print $ eligible 300 -- Just True
print $ eligible 299 -- Nothing
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
print $ [ (x,y,z) | x <- allCases,
y <- allCases,
z <- allCases,
4*x + 2*y < z ]
shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (x) xs))
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
main = do
putStrLn "take 10 shuffle"
print $ take 10 shuffle
putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
print $ treeTakeDepth 4 (treeFromList shuffle)
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.
04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
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
treeFromList :: (Ord a, Show a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x left right
where
left = treeFromList $ safefilter (x) xs
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)
main = do
putStrLn "take 10 shuffle"
print $ take 10 shuffle
putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
print $ treeTakeDepth 8 (treeFromList $ shuffle)
safefilter' f l = if filter f (take 10000 l) == [] then [] else filter f lExplain 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. Disclaimer, this is only a conjecture.
treeFromList' [] n = Empty
treeFromList' (x:xs) n = Node x left right
where
left = treeFromList' (safefilter' (x) xs (f n)
f = ???
04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs