----- isHidden: false menupriority: 1 kind: article created_at: 2012-02-08T15:17:53+02:00 title: Haskell comme un vrai! subtitle: Haskell à s'en faire griller les neurones author_name: Yann Esposito author_uri: yannesposito.com tags: - Haskell - programming - functional - tutorial ----- blogimage("magritte_pleasure_principle.jpg","Magritte pleasure principle") begindiv(intro) %tlal Un tutoriel très court mais très dense pour apprendre Haskell. >
> runhaskell filename.lhs >> > Certain ne marcheront pas, mais la majorité vous donneront un résultat. > Vous devriez voir un lien juste en dessous. 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 variables, nor for loop.
One solution to achieve the same result without loops is to use recursion.
> _Remark_:
> Recursion is generally perceived as slow in imperative languages.
> But it is generally not the case in functional programming.
> Most of the time Haskell will handle recursive functions efficiently.
Here is a `C` version of the recursive function.
Note that for simplicity, I assume the int list ends 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 into Haskell.
But before, I need to introduce three simple but useful functions we will use:
even :: Integral a => a -> Bool
head :: [a] -> a
tail :: [a] -> [a]
`even` verifies 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 elements of a list, except the first:
tail :: [a] -> [a]
tail [1,2,3] ⇒ [2,3]
tail [3] ⇒ []
tail [] ⇒ ERROR
Note 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'm cheating. 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 seem 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 goes even further.
It is also able to inspect the inner data of a complex value.
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
with
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 a loop.
We will use the `foldl` function to accumulate a value.
The function `foldl` captures 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 pushes it 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` becomes:
-- 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 study it 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 corresponds 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 some parts 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` with 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 amount of code necessary in C:
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:
#include
#include
using namespace std;
template
T square(T x)
{
return x*x;
}
int main() {
// int
int sqr_of_five = square(5);
cout << sqr_of_five << endl;
// double
cout << (double)square(5.3) << endl;
// complex
cout << square( complex(5,3) )
<< endl;
return 0;
}
C++ does a far better job than C.
For more complex function the syntax can be hard to follow:
look at
[this article](http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/)
for example.
In C++ you must declare that a function can work with different types.
In Haskell this is the opposite.
The function will be as general as possible by default.
Type inference gives Haskell the feeling of freedom that dynamically
typed languages provide.
But unlike dynamically typed languages, most errors 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 an 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 functions 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 of how to show a binary tree.
Don't worry about the apparent complexity.
I made a lot of improvements in order to display even stranger 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
-- shows a tree and starts each line with pref
-- We don't display the 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 children non empty
treeshow pref (Node x left right) =
(pshow pref x) ++ "\n" ++
(showSon pref "|--" "| " left) ++ "\n" ++
(showSon pref "`--" " " right)
-- shows a tree using some prefixes to make it nice
showSon pref before next t =
pref ++ before ++ treeshow (pref ++ next) t
-- pshow replaces "\n" by "\n"++pref
pshow pref x = replace '\n' ("\n"++pref) (show x)
-- replaces one 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'
~~~
Notice 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 trees containing not only integers, strings and chars, 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 compiles, runs and stops giving the following result:
~~~
< 0
: |-- 0
: | |-- 0
: | | |-- 0
: | | `-- 0
: | `-- 0
: | |-- 0
: | `-- 0
: `-- 0
: |-- 0
: | |-- 0
: | `-- 0
: `-- 0
: |-- 0
: `-- 0
~~~
Just to heat up 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 pay attention to the effect of the `<-` symbol. ~~~ do x <- something ~~~ If `something :: IO a` then `x :: a`. Another important note about using `IO`. All lines in a do block must be of 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 kinds of line will correspond to two different ways of sequencing actions. The meaning of this sentence should be clearer by 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 impure code 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 noticed 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 functions of your language.
For example, you can read and write a file in any function.
The fact that a file exists or not can be seen as different states of the world.
For Haskell this state is not hidden.
It is explicitly said `main` is a function that _potentially_ changes the state of the world.
Its type is then something like:
main :: World -> World
Not all functions may have access to this variable.
Those which have access to this variable are impure.
Functions to which the world variable isn't provided are pure[^032001].
[^032001]: There are some _unsafe_ exceptions to this rule. But you shouldn't see such use on a real application except maybe for debugging purpose.
Haskell considers the state of the world as an input variable to `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 note that all functions which have side effects must have the type:
World -> (a,World)
Where `a` is the type of the result.
For example, a `getChar` function should have the type `World -> (Char,World)`.
Another thing to note is the trick to fix the order of evaluation.
In Haskell, in order to evaluate `f a b`, you 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 do 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 languages is awkward!
Fortunately, there is 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 note that we always follow the same usage pattern:
let (y,w1) = action1 w0 in
let (z,w2) = action2 w1 in
let (t,w3) = action3 w2 in
...
Each action can take from 0 to n 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 patterns to consider:
>
> ~~~
> let (x,w1) = action1 w0 in
> let (y,w2) = action2 x 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 do 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 takes a world as parameter and returns a couple `(String,World)`.
Which can be summarized 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 interesting.
It takes one 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 returns a couple of type `((),World)`.
This means it changes the state of the world, but doesn't yield anymore data.
This type helps 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 it 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 an 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 see 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 notice something?
Yes, no temporary World variables are 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 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 about the lines 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 simplify 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 values 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 of 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 functions of the class must be provided for this type.
> - In this particular example of type class, the type `m` must be a type that takes an argument.
> for example `IO a`, but also `Maybe a`, `[a]`, etc...
> - To be a useful monad, your function must obey some rules.
> 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 programs have to offer.
Left as an exercise to the reader:
- Prove the existence of a number `n` so that `treeTakeDepth n (treeFromList shuffle)` will enter an infinite loop.
- Find an upper bound for `n`.
- Prove there is no `shuffle` list so 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
## Thanks
Thanks to [`/r/haskell`](http://reddit.com/r/haskell) and
[`/r/programming`](http://reddit.com/r/programming).
Your comment were most than welcome.
Particularly, I want to thank [Emm](https://github.com/Emm) a thousand times
for the time he spent on correcting my English.
Thank you man.