her.esy.fun/src/drafts/Haskell-the-Hard-Way.org
Yann Esposito (Yogsototh) 5a96e41469
save
2019-07-28 18:44:25 +02:00

3773 lines
100 KiB
Org Mode
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+TITLE: Learn Haskell Fast and Hard
#+AUTHOR: Yann Esposito
#+KEYWORDS: Haskell, programming, functional, tutorial
#+OPTIONS: auto-id:t
#+PROPERTY: eval no
blogimage("magritte_pleasure_principle.jpg","Magritte pleasure
principle")
%tldr A very short and dense tutorial for learning Haskell.
Thanks to:
- [[https://plus.google.com/u/0/113751420744109290534][Oleg Taykalo]]
you can find a Russian translation here:
[[http://habrahabr.ru/post/152889/][Part 1]] /&/
[[http://habrahabr.ru/post/153383/][Part 2]],
- [[http://silly-bytes.blogspot.fr][Daniel Campoverde]] for the Spanish
translation here:
[[http://silly-bytes.blogspot.fr/2016/06/aprende-haskell-rapido-y-dificil_29.html][Aprende
Haskell rápido y difícil]],
- [[http://github.com/joom][Joomy Korkut]] for the Turkish translation
here: [[https://github.com/joom/zor-yoldan-haskell][Zor Yoldan
Haskell]].
I really believe all developers should learn Haskell. I don't think
everyone needs to be super Haskell ninjas, but they should at least
discover what Haskell has to offer. Learning Haskell opens your mind.
Mainstream languages share the same foundations:
- variables
- loops
- pointers[fn:1]
- data structures, objects and classes (for most)
Haskell is very different. The language uses a lot of concepts I had
never heard about before. Many of those concepts will help you become a
better programmer.
But learning Haskell can be hard. It was for me. In this article I try
to provide what I lacked during my learning.
This article will certainly be hard to follow. This is on purpose. There
is no shortcut to learning Haskell. It is hard and challenging. But I
believe this is a good thing. It is because it is hard that Haskell is
interesting.
The conventional method to learning Haskell is to read two books. First
[[http://learnyouahaskell.com]["Learn You a Haskell"]] and just after
[[http://www.realworldhaskell.org]["Real World Haskell"]]. I also
believe this is the right way to go. But to learn what Haskell is all
about, you'll have to read them in detail.
In contrast, this article is a very brief and dense overview of all
major aspects of Haskell. I also added some information I lacked while I
learned Haskell.
The article contains five parts:
- Introduction: a short example to show Haskell can be friendly.
- Basic Haskell: Haskell syntax, and some essential notions.
- Hard Difficulty Part:
- Functional style; a progressive 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
- Appendix:
- More on infinite tree; a more math oriented discussion about
infinite trees
#+BEGIN_QUOTE
Note: Each time you see a separator with a filename ending in =.lhs=
you can click the filename to get this file. If you save the file as
=filename.lhs=, you can run it with
#+BEGIN_SRC bash
runhaskell filename.lhs
#+END_SRC
Some might not work, but most will. You should see a link just below.
#+END_QUOTE
* Introduction
:PROPERTIES:
:CUSTOM_ID: introduction
:END:
** Install
:PROPERTIES:
:CUSTOM_ID: install
:END:
blogimage("Haskell-logo.png", "Haskell logo")
There are different way to install Haskell, I would recommend to use
[[https://haskellstack.org][=stack=]].
There are other way to install Haskell on your system you could visit,
you can learn more about it by visiting
[[https://haskell.org][haskell.org]] or
[[https://haskell-lang.org][haskell-lang.org]]
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 programs.
** Don't be afraid
:PROPERTIES:
:CUSTOM_ID: don't-be-afraid
:END:
blogimage("munch_TheScream.jpg","The Scream")
Many books/articles about Haskell start by introducing some esoteric
formula (quick sort, Fibonacci, etc...). I will do 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
to the mandatory "Hello World".
#+BEGIN_SRC haskell :eval never-export
main = putStrLn "Hello World!"
#+END_SRC
To run it, you can save this code in a =hello.hs= and:
#+BEGIN_EXAMPLE
~ runhaskell ./hello.hs
Hello World!
#+END_EXAMPLE
or if you use =stack= first run =stack setup= and then:
#+BEGIN_EXAMPLE
~ stack runhaskell ./hello.hs
Hello World!
#+END_EXAMPLE
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:
#+BEGIN_EXAMPLE
~ runhaskell 00_hello_world.lhs
Hello World!
#+END_EXAMPLE
01_basic/10_Introduction/00_hello_world.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/10_hello_you.lhs
Now, a program asking your name and replying "Hello" using the name you
entered:
#+BEGIN_SRC haskell :eval never-export
main = do
print "What is your name?"
name <- getLine
print ("Hello " ++ name ++ "!")
#+END_SRC
First, let us compare this with similar programs in a few imperative
languages:
#+BEGIN_SRC python
# Python
print "What is your name?"
name = raw_input()
print "Hello %s!" % name
#+END_SRC
#+BEGIN_SRC ruby
# Ruby
puts "What is your name?"
name = gets.chomp
puts "Hello #{name}!"
#+END_SRC
#+BEGIN_SRC C
// 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;
}
#+END_SRC
The structure is the same, but there are some syntax differences. The
main 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
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/20_very_basic.lhs
#+BEGIN_HTML
<h3 id="very-basic-haskell">
#+END_HTML
Very basic Haskell
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("picasso_owl.jpg","Picasso minimal owl")
Before continuing you need to be warned about some essential properties
of Haskell.
/Functional/
Haskell is a functional language. If you have an imperative language
background, 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 functions won't modify anything in the outside world.
This means they 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 occur and where your code is pure. Also, it will be far
easier to reason about your program. Most bugs will be prevented in the
pure parts of your program.
Furthermore, pure functions follow a fundamental law in Haskell:
#+BEGIN_QUOTE
Applying a function with the same parameters always returns the same
value.
#+END_QUOTE
/Laziness/
Laziness by default is a very uncommon language design. By default,
Haskell evaluates something only when it is needed. In consequence, it
provides a very elegant way to manipulate infinite structures, for
example.
A last warning about how you should read Haskell code. For me, it is
like reading scientific papers. Some parts 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 meet a =>>==, =<$>=, =<-= or any other weird symbol, just ignore
them and follows the flow of the code.
#+BEGIN_HTML
<h4 id="function-declaration">
#+END_HTML
Function declaration
#+BEGIN_HTML
</h4>
#+END_HTML
You might be used to declaring functions like this:
In =C=:
#+BEGIN_SRC C
int f(int x, int y) {
return x*x + y*y;
}
#+END_SRC
In JavaScript:
#+BEGIN_EXAMPLE
function f(x,y) {
return x*x + y*y;
}
#+END_EXAMPLE
in Python:
#+BEGIN_SRC python
def f(x,y):
return x*x + y*y
#+END_SRC
in Ruby:
#+BEGIN_SRC ruby
def f(x,y)
x*x + y*y
end
#+END_SRC
In Scheme:
#+BEGIN_SRC scheme
(define (f x y)
(+ (* x x) (* y y)))
#+END_SRC
Finally, the Haskell way is:
#+BEGIN_SRC haskell :eval never-export
f x y = x*x + y*y
#+END_SRC
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 out for
these objects.
#+BEGIN_HTML
<h4 id="a-type-example">
#+END_HTML
A Type Example
#+BEGIN_HTML
</h4>
#+END_HTML
Although it is not mandatory, type information for functions is usually
made explicit. It's not mandatory because the compiler is smart enough
to discover it for you. It's a good idea because it indicates intent and
understanding.
Let's play a little. We declare the type using =::=
#+BEGIN_SRC haskell :eval never-export
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2 3)
#+END_SRC
#+BEGIN_EXAMPLE
~ runhaskell 20_very_basic.lhs
13
#+END_EXAMPLE
01_basic/10_Introduction/20_very_basic.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/21_very_basic.lhs
Now try
#+BEGIN_SRC haskell :eval never-export
f :: Int -> Int -> Int
f x y = x*x + y*y
main = print (f 2.3 4.2)
#+END_SRC
You should get this error:
#+BEGIN_EXAMPLE
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)
#+END_EXAMPLE
The problem: =4.2= isn't an Int.
01_basic/10_Introduction/21_very_basic.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/22_very_basic.lhs
The solution: don't declare a type for =f= for the moment and let
Haskell infer the most general type for us:
#+BEGIN_SRC haskell :eval never-export
f x y = x*x + y*y
main = print (f 2.3 4.2)
#+END_SRC
It works! Luckily, we don't have to declare a new function for every
single 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 has found
for us, just launch ghci:
#+BEGIN_HTML
<pre><span class="low">
%</span> ghci<span class="low"><code>
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></code></span> let f x y = x*x + y*y
<span class="low"><code>Prelude></code></span> :type f
<code>f :: Num a => a -> a -> a</code>
</pre>
#+END_HTML
Uh? What is this strange type?
#+BEGIN_EXAMPLE
Num a => a -> a -> a
#+END_EXAMPLE
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
values. For example =Int=, =Integer=, =Float=...
So instead of having a forced type like in =C= and having to declare a
function for =int=, =long=, =float=, =double=, etc., we declare only one
function like in a dynamically typed language.
This is sometimes called parametric polymorphism. It's also called
having your cake and eating it too.
Generally =a= can be any type, for example a =String= or 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 which 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 a parameter.
More precisely =f 3 4= is equivalent to =(f 3) 4=. Note =f 3= is a
function:
#+BEGIN_EXAMPLE
f :: Num a => a -> a -> a
g :: Num a => a -> a
g = f 3
g y ⇔ 3*3 + y*y
#+END_EXAMPLE
Another notation exists for functions. The lambda notation allows us to
create functions without assigning them a name. We call them anonymous
functions. We could also have written:
#+BEGIN_EXAMPLE
g = \y -> 3*3 + y*y
#+END_EXAMPLE
The =\= is used because it looks like =λ= and is ASCII.
If you are not used to functional programming your brain should be
starting to heat up. It is time to make a real application.
01_basic/10_Introduction/22_very_basic.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/23_very_basic.lhs
But just before that, we should verify the type system works as
expected:
#+BEGIN_SRC haskell :eval never-export
f :: Num a => a -> a -> a
f x y = x*x + y*y
main = print (f 3 2.4)
#+END_SRC
It works, because, =3= is a valid representation both for Fractional
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
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/10_Introduction/24_very_basic.lhs
If we force our function to work with different types, it will fail:
#+BEGIN_SRC haskell :eval never-export
f :: Num a => a -> a -> a
f x y = x*x + y*y
x :: Int
x = 3
y :: Float
y = 2.4
-- won't work because type x ≠ type y
main = print (f x y)
#+END_SRC
The compiler complains. The two parameters must have the same type.
If you believe that this is a bad idea, and that the compiler should
make the transformation from one type to another for you, you should
really watch this great (and funny) video:
[[https://www.destroyallsoftware.com/talks/wat][WAT]]
01_basic/10_Introduction/24_very_basic.lhs
#+BEGIN_HTML
<h2 id="essential-haskell">
#+END_HTML
Essential Haskell
#+BEGIN_HTML
</h2>
#+END_HTML
blogimage("kandinsky_gugg.jpg","Kandinsky Gugg")
I suggest that you skim this part. Think of it as a reference. Haskell
has a lot of features. A lot of information is missing here. Come back
here if the 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 the return value of an expression is.
#+BEGIN_HTML
<h3 id="notations">
#+END_HTML
Notations
#+BEGIN_HTML
</h3>
#+END_HTML
#+BEGIN_HTML
<h5 id="arithmetic">
#+END_HTML
Arithmetic
#+BEGIN_HTML
</h5>
#+END_HTML
#+BEGIN_EXAMPLE
3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3)
#+END_EXAMPLE
#+BEGIN_HTML
<h5 id="logic">
#+END_HTML
Logic
#+BEGIN_HTML
</h5>
#+END_HTML
#+BEGIN_EXAMPLE
True || False ⇒ True
True && False ⇒ False
True == False ⇒ False
True /= False ⇒ True (/=) is the operator for different
#+END_EXAMPLE
#+BEGIN_HTML
<h5 id="powers">
#+END_HTML
Powers
#+BEGIN_HTML
</h5>
#+END_HTML
#+BEGIN_EXAMPLE
x^n for n an integral (understand Int or Integer)
x**y for y any kind of number (Float for example)
#+END_EXAMPLE
=Integer= has no limit except the capacity of your machine:
#+BEGIN_EXAMPLE
4^103
102844034832575377634685573909834406561420991602098741459288064
#+END_EXAMPLE
Yeah! And also rational numbers FTW! But you need to import the module
=Data.Ratio=:
#+BEGIN_EXAMPLE
$ ghci
....
Prelude> :m Data.Ratio
Data.Ratio> (11 % 15) * (5 % 3)
11 % 9
#+END_EXAMPLE
#+BEGIN_HTML
<h5 id="lists">
#+END_HTML
Lists
#+BEGIN_HTML
</h5>
#+END_HTML
#+BEGIN_EXAMPLE
[] ⇔ 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]
#+END_EXAMPLE
#+BEGIN_HTML
<h5 id="strings">
#+END_HTML
Strings
#+BEGIN_HTML
</h5>
#+END_HTML
In Haskell strings are list of =Char=.
#+BEGIN_EXAMPLE
'a' :: Char
"a" :: [Char]
"" ⇔ []
"ab" ⇔ ['a','b'] ⇔ 'a':"b" ⇔ 'a':['b'] ⇔ 'a':'b':[]
"abc" ⇔ "ab"++"c"
#+END_EXAMPLE
#+BEGIN_QUOTE
/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 a stream of ASCII char, you should use =Data.ByteString=.
#+END_QUOTE
#+BEGIN_HTML
<h5 id="tuples">
#+END_HTML
Tuples
#+BEGIN_HTML
</h5>
#+END_HTML
The type of couple is =(a,b)=. Elements in a tuple can have different
types.
#+BEGIN_EXAMPLE
-- All these tuples 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
#+END_EXAMPLE
#+BEGIN_HTML
<h5 id="deal-with-parentheses">
#+END_HTML
Deal with parentheses
#+BEGIN_HTML
</h5>
#+END_HTML
To remove some parentheses you can use two functions: =($)= and =(.)=.
#+BEGIN_EXAMPLE
-- 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))
#+END_EXAMPLE
#+BEGIN_HTML
<hr/>
#+END_HTML
01_basic/20_Essential_Haskell/10a_Functions.lhs
#+BEGIN_HTML
<h3 id="useful-notations-for-functions">
#+END_HTML
Useful notations for functions
#+BEGIN_HTML
</h3>
#+END_HTML
Just a reminder:
#+BEGIN_EXAMPLE
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
#+END_EXAMPLE
Remember that 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/
#+BEGIN_SRC haskell :eval never-export
square :: Num a => a -> a
square x = x^2
#+END_SRC
Note =^= uses infix notation. For each infix operator there its
associated prefix notation. You just have to put it inside parenthesis.
#+BEGIN_SRC haskell :eval never-export
square' x = (^) x 2
square'' x = (^2) x
#+END_SRC
We can remove =x= in the left and right side! It's called η-reduction.
#+BEGIN_SRC haskell :eval never-export
square''' = (^2)
#+END_SRC
Note we can declare functions with ='= in their name. Here:
#+BEGIN_QUOTE
=square= ⇔ =square'= ⇔ =square''= ⇔ =square'''=
#+END_QUOTE
/Tests/
An implementation of the absolute function.
#+BEGIN_SRC haskell :eval never-export
absolute :: (Ord a, Num a) => a -> a
absolute x = if x >= 0 then x else -x
#+END_SRC
Note: the =if .. then .. else= Haskell notation is more like the =¤?¤:¤=
C operator. You cannot forget the =else=.
Another equivalent version:
#+BEGIN_SRC haskell :eval never-export
absolute' x
| x >= 0 = x
| otherwise = -x
#+END_SRC
#+BEGIN_QUOTE
Notation warning: indentation is /important/ in Haskell. Like in
Python, bad indentation can break your code!
#+END_QUOTE
#+BEGIN_SRC haskell :eval never-export
main = do
print $ square 10
print $ square' 10
print $ square'' 10
print $ square''' 10
print $ absolute 10
print $ absolute (-10)
print $ absolute' 10
print $ absolute' (-10)
#+END_SRC
01_basic/20_Essential_Haskell/10a_Functions.lhs
#+BEGIN_HTML
<h2 id="hard-part">
#+END_HTML
Hard Part
#+BEGIN_HTML
</h2>
#+END_HTML
The hard part can now begin.
#+BEGIN_HTML
<h3 id="functional-style">
#+END_HTML
Functional style
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("hr_giger_biomechanicallandscape_500.jpg","Biomechanical
Landscape by H.R. Giger")
In this section, I will give a short example of the impressive
refactoring ability provided by Haskell. We will select a problem and
solve it in 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 solve the following problem:
#+BEGIN_QUOTE
Given a list of integers, return the sum of the even numbers in the
list.
example: =[1,2,3,4,5] ⇒ 2 + 4 ⇒ 6=
#+END_QUOTE
To show differences between functional and imperative approaches, I'll
start by providing an imperative solution (in JavaScript):
#+BEGIN_EXAMPLE
function evenSum(list) {
var result = 0;
for (var i=0; i< list.length ; i++) {
if (list[i] % 2 ==0) {
result += list[i];
}
}
return result;
}
#+END_EXAMPLE
In Haskell, by contrast, we don't have variables or a for loop. One
solution to achieve the same result without loops is to use recursion.
#+BEGIN_QUOTE
/Remark/: Recursion is generally perceived as slow in imperative
languages. But this is generally not the case in functional
programming. Most of the time Haskell will handle recursive functions
efficiently.
#+END_QUOTE
Here is a =C= version of the recursive function. Note that for
simplicity I assume the int list ends with the first =0= value.
#+BEGIN_SRC C
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);
}
}
}
#+END_SRC
Keep this code in mind. We will translate it into Haskell. First,
however, I need to introduce three simple but useful functions we will
use:
#+BEGIN_SRC haskell :eval never-export
even :: Integral a => a -> Bool
head :: [a] -> a
tail :: [a] -> [a]
#+END_SRC
=even= verifies if a number is even.
#+BEGIN_SRC haskell :eval never-export
even :: Integral a => a -> Bool
even 3 False
even 2 True
#+END_SRC
=head= returns the first element of a list:
#+BEGIN_SRC haskell :eval never-export
head :: [a] -> a
head [1,2,3] 1
head [] ERROR
#+END_SRC
=tail= returns all elements of a list, except the first:
#+BEGIN_SRC haskell :eval never-export
tail :: [a] -> [a]
tail [1,2,3] [2,3]
tail [3] []
tail [] ERROR
#+END_SRC
Note that for any non empty list =l=, =l ⇔ (head l):(tail l)=
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/11_Functions.lhs
The first Haskell solution. The function =evenSum= returns the sum of
all even numbers in a list:
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
To test a function you can use =ghci=:
#+BEGIN_HTML
<pre>
% ghci
<span class="low">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&gt;</span> :load 11_Functions.lhs
<span class="low">[1 of 1] Compiling Main ( 11_Functions.lhs, interpreted )
Ok, modules loaded: Main.
*Main&gt;</span> evenSum [1..5]
6
</pre>
#+END_HTML
Here is an example of execution[fn:2]:
#+BEGIN_HTML
<pre>
*Main> evenSum [1..5]
accumSum 0 [1,2,3,4,5]
<span class="yellow">1 is odd</span>
accumSum 0 [2,3,4,5]
<span class="yellow">2 is even</span>
accumSum (0+2) [3,4,5]
<span class="yellow">3 is odd</span>
accumSum (0+2) [4,5]
<span class="yellow">2 is even</span>
accumSum (0+2+4) [5]
<span class="yellow">5 is odd</span>
accumSum (0+2+4) []
<span class="yellow">l == []</span>
0+2+4
0+6
6
</pre>
#+END_HTML
Coming from an imperative language all should seem right. In fact, many
things can be improved here. First, we can generalize the type.
#+BEGIN_SRC haskell :eval never-export
evenSum :: Integral a => [a] -> a
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
main = do print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/11_Functions.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/12_Functions.lhs
Next, we can use sub functions using =where= or =let=. This way our
=accumSum= function won't pollute the namespace of our module.
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
main = print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/12_Functions.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/13_Functions.lhs
Next, we can use pattern matching.
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
What is pattern matching? Use values instead of general parameter
names[fn:3].
Instead of saying: =foo l = if l == [] then <x> else <y>= You simply
state:
#+BEGIN_SRC haskell :eval never-export
foo [] = <x>
foo l = <y>
#+END_SRC
But pattern matching goes even further. It is also able to inspect the
inner data of a complex value. We can replace
#+BEGIN_SRC haskell :eval never-export
foo l = let x = head l
xs = tail l
in if even x
then foo (n+x) xs
else foo n xs
#+END_SRC
with
#+BEGIN_SRC haskell :eval never-export
foo (x:xs) = if even x
then foo (n+x) xs
else foo n xs
#+END_SRC
This is a very useful feature. It makes our code both terser and easier
to read.
#+BEGIN_SRC haskell :eval never-export
main = print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/13_Functions.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/14_Functions.lhs
In Haskell you can simplify function definitions by η-reducing them. For
example, instead of writing:
#+BEGIN_SRC haskell :eval never-export
f x = (some expresion) x
#+END_SRC
you can simply write
#+BEGIN_SRC haskell :eval never-export
f = some expression
#+END_SRC
We use this method to remove the =l=:
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
main = print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/14_Functions.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/15_Functions.lhs
#+BEGIN_HTML
<h4 id="higher-order-functions">
#+END_HTML
Higher Order Functions
#+BEGIN_HTML
</h4>
#+END_HTML
blogimage("escher_polygon.png","Escher")
To make things even better we should use higher order functions. What
are these beasts? Higher order functions are functions taking functions
as parameters.
Here are some examples:
#+BEGIN_SRC haskell :eval never-export
filter :: (a -> Bool) -> [a] -> [a]
map :: (a -> b) -> [a] -> [b]
foldl :: (a -> b -> a) -> a -> [b] -> a
#+END_SRC
Let's proceed by small steps.
#+BEGIN_SRC haskell :eval never-export
-- Version 5
evenSum l = mysum 0 (filter even l)
where
mysum n [] = n
mysum n (x:xs) = mysum (n+x) xs
#+END_SRC
where
#+BEGIN_SRC haskell :eval never-export
filter even [1..10] [2,4,6,8,10]
#+END_SRC
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 technique to accomplish the same thing
as a loop. We will use the =foldl= function to accumulate a value as we
pass through the list. The function =foldl= captures a general coding
pattern:
#+BEGIN_HTML
<pre>
myfunc list = foo <span class="blue">initialValue</span> <span class="green">list</span>
foo accumulated [] = accumulated
foo tmpValue (x:xs) = foo (<span class="yellow">bar</span> tmpValue x) xs
</pre>
#+END_HTML
Which can be replaced by:
#+BEGIN_HTML
<pre>
myfunc list = foldl <span class="yellow">bar</span> <span class="blue">initialValue</span> <span class="green">list</span>
</pre>
#+END_HTML
If you really want to know how the magic works, here is the definition
of =foldl=:
#+BEGIN_SRC haskell :eval never-export
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
foldl f z [x1,...xn]
f (... (f (f z x1) x2) ...) xn
#+END_SRC
But as Haskell is lazy, it doesn't evaluate =(f z x)= and simply pushes
it onto 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'= were identical.
Now our new version of =evenSum= becomes:
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
We can also simplify this by using directly a lambda notation. This way
we don't have to create the temporary name =mysum=.
#+BEGIN_SRC haskell :eval never-export
-- 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)
#+END_SRC
And of course, we note that
#+BEGIN_SRC haskell :eval never-export
(\x y -> x+y) (+)
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
main = print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/15_Functions.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/16_Functions.lhs
Finally
#+BEGIN_SRC haskell :eval never-export
-- Version 8
import Data.List (foldl')
evenSum :: Integral a => [a] -> a
evenSum l = foldl' (+) 0 (filter even l)
#+END_SRC
=foldl'= isn't the easiest function to grasp. If you are not used to it,
you should study it a bit.
To help you understand what's going on here, let's look at a step by
step evaluation:
#+BEGIN_HTML
<pre>
<span class="yellow">evenSum [1,2,3,4]</span>
⇒ foldl' (+) 0 (<span class="yellow">filter even [1,2,3,4]</span>)
⇒ <span class="yellow">foldl' (+) 0 <span class="blue">[2,4]</span></span>
⇒ <span class="blue">foldl' (+) (<span class="yellow">0+2</span>) [4]</span>
⇒ <span class="yellow">foldl' (+) <span class="blue">2</span> [4]</span>
⇒ <span class="blue">foldl' (+) (<span class="yellow">2+4</span>) []</span>
⇒ <span class="yellow">foldl' (+) <span class="blue">6</span> []</span>
⇒ <span class="blue">6</span>
</pre>
#+END_HTML
Another useful higher order function is =(.)=. The =(.)= function
corresponds to mathematical composition.
#+BEGIN_SRC haskell :eval never-export
(f . g . h) x f ( g (h x))
#+END_SRC
We can take advantage of this operator to η-reduce our function:
#+BEGIN_SRC haskell :eval never-export
-- Version 9
import Data.List (foldl')
evenSum :: Integral a => [a] -> a
evenSum = (foldl' (+) 0) . (filter even)
#+END_SRC
Also, we could rename some parts to make it clearer:
#+BEGIN_SRC haskell :eval never-export
-- Version 10
import Data.List (foldl')
sum' :: (Num a) => [a] -> a
sum' = foldl' (+) 0
evenSum :: Integral a => [a] -> a
evenSum = sum' . (filter even)
#+END_SRC
It is time to discuss the direction our code has moved as we introduced
more functional idioms. What did we gain by using higher order
functions?
At first, you might think the main difference is terseness. But in fact,
it has more to do with better thinking. Suppose we want to modify our
function slightly, for example, to get the sum of all even squares of
elements of the list.
#+BEGIN_EXAMPLE
[1,2,3,4] ▷ [1,4,9,16] ▷ [4,16] ▷ 20
#+END_EXAMPLE
Updating version 10 is extremely easy:
#+BEGIN_SRC haskell :eval never-export
squareEvenSum = sum' . (filter even) . (map (^2))
squareEvenSum' = evenSum . (map (^2))
#+END_SRC
We just had to add another "transformation function"[^0216].
#+BEGIN_EXAMPLE
map (^2) [1,2,3,4] ⇔ [1,4,9,16]
#+END_EXAMPLE
The =map= function simply applies a function to all the elements of a
list.
We didn't have to modify anything /inside/ the function definition. This
makes the code more modular. But in addition you can think more
mathematically about your function. You can also use your function
interchangably with others, as needed. That is, you can compose, map,
fold, filter using your new function.
Modifying version 1 is left as an exercise to the reader ☺.
If you believe we have reached the end of generalization, then know you
are very wrong. For example, there is a way to not only use this
function on lists but on any recursive type. If you want to know how, I
suggest you to read this quite fun article:
[[http://eprints.eemcs.utwente.nl/7281/01/db-utwente-40501F46.pdf][Functional
Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer,
Fokkinga and Paterson]].
This example should show you how great pure functional programming is.
Unfortunately, using pure functional programming isn't well suited to
all usages. Or at least such a language hasn't been found yet.
One of the great powers of Haskell is the ability to create DSLs (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 to grasp when
first learning Haskell. A lot of effort tends to go into explaining the
superiority of the functional approach. Then when you start using an
imperative style with Haskell, it can be hard to understand when and how
to use it.
But before talking about this Haskell super-power, we must talk about
another essential aspect of Haskell: /Types/.
#+BEGIN_SRC haskell :eval never-export
main = print $ evenSum [1..10]
#+END_SRC
02_Hard_Part/16_Functions.lhs
#+BEGIN_HTML
<h3 id="types">
#+END_HTML
Types
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("salvador-dali-the-madonna-of-port-lligat.jpg","Dali, the
madonna of port Lligat")
#+BEGIN_QUOTE
%tldr
- =type Name = AnotherType= is just an alias and the compiler doesn't
mark any difference between =Name= and =AnotherType=.
- =data Name = NameConstructor AnotherType= does mark a difference.
- =data= can construct structures which can be recursives.
- =deriving= is magic and creates functions for you.
#+END_QUOTE
In Haskell, types are strong and static.
Why is this important? It will help you /greatly/ to avoid mistakes. In
Haskell, most bugs are caught during the compilation of your program.
And the main reason is because of the type inference during compilation.
Type inference makes it easy to detect where you used the wrong
parameter at the wrong place, for example.
#+BEGIN_HTML
<h4 id="type-inference">
#+END_HTML
Type inference
#+BEGIN_HTML
</h4>
#+END_HTML
Static typing is generally essential for fast execution. But most
statically typed languages are bad at generalizing concepts. Haskell's
saving grace is that it can /infer/ types.
Here is a simple example, the =square= function in Haskell:
#+BEGIN_SRC haskell :eval never-export
square x = x * x
#+END_SRC
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:
#+BEGIN_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
#+END_EXAMPLE
=x :+ y= is the notation for the complex (x + iy).
Now compare with the amount of code necessary in C:
#+BEGIN_SRC 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);
#+END_SRC
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, C++ templates:
#+BEGIN_EXAMPLE
#include <iostream>
#include <complex>
using namespace std;
template<typename T>
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<double>(5,3) )
<< endl;
return 0;
}
#+END_EXAMPLE
C++ does a far better job than C in this regard. But for more complex
functions the syntax can be hard to follow: see
[[http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/][this
article]] for example.
In C++ you must declare that a function can work with different types.
In Haskell, the opposite is the case. 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 run time. Generally, in Haskell:
#+BEGIN_QUOTE
"if it compiles it certainly does what you intended"
#+END_QUOTE
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/21_Types.lhs
#+BEGIN_HTML
<h4 id="type-construction">
#+END_HTML
Type construction
#+BEGIN_HTML
</h4>
#+END_HTML
You can construct your own types. First, you can use aliases or type
synonyms.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
02_Hard_Part/21_Types.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
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:
#+BEGIN_SRC haskell :eval never-export
putStrLn $ showInfos color name
#+END_SRC
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=.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
Now if you switch parameters of =showInfos=, the compiler complains! So
this is a potential mistake you will never make again and the only price
is to be more verbose.
Also notice that constructors are functions:
#+BEGIN_SRC haskell :eval never-export
NameConstr :: String -> Name
ColorConstr :: String -> Color
#+END_SRC
The syntax of =data= is mainly:
#+BEGIN_SRC haskell :eval never-export
data TypeName = ConstructorName [types]
| ConstructorName2 [types]
| ...
#+END_SRC
Generally the usage is to use the same name for the DataTypeName and
DataTypeConstructor.
Example:
#+BEGIN_SRC haskell :eval never-export
data Complex a = Num a => Complex a a
#+END_SRC
Also you can use the record syntax:
#+BEGIN_SRC haskell :eval never-export
data DataTypeName = DataConstructor {
field1 :: [type of field1]
, field2 :: [type of field2]
...
, fieldn :: [type of fieldn] }
#+END_SRC
And many accessors are made for you. Furthermore you can use another
order when setting values.
Example:
#+BEGIN_SRC haskell :eval never-export
data Complex a = 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
#+END_SRC
02_Hard_Part/22_Types.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/23_Types.lhs
#+BEGIN_HTML
<h4 id="recursive-type">
#+END_HTML
Recursive type
#+BEGIN_HTML
</h4>
#+END_HTML
You already encountered a recursive type: lists. You can re-create
lists, but with a more verbose syntax:
#+BEGIN_SRC haskell :eval never-export
data List a = Empty | Cons a (List a)
#+END_SRC
If you really want to use an easier syntax you can use an infix name for
constructors.
#+BEGIN_SRC haskell :eval never-export
infixr 5 :::
data List a = Nil | a ::: (List a)
#+END_SRC
The number after =infixr= gives the precedence.
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.
#+BEGIN_SRC haskell :eval never-export
infixr 5 :::
data List a = Nil | a ::: (List a)
deriving (Show,Read,Eq,Ord)
#+END_SRC
When you add =deriving (Show)= to your data declaration, Haskell creates
a =show= function for you. We'll see soon how you can use your own
=show= function.
#+BEGIN_SRC haskell :eval never-export
convertList [] = Nil
convertList (x:xs) = x ::: convertList xs
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
main = do
print (0 ::: 1 ::: Nil)
print (convertList [0,1])
#+END_SRC
This prints:
#+BEGIN_EXAMPLE
0 ::: (1 ::: Nil)
0 ::: (1 ::: Nil)
#+END_EXAMPLE
02_Hard_Part/23_Types.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/30_Trees.lhs
*** Trees
:PROPERTIES:
:CUSTOM_ID: trees
:END:
blogimage("magritte-l-arbre.jpg","Magritte, l'Arbre")
We'll just give another standard example: binary trees.
#+BEGIN_SRC haskell :eval never-export
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Show)
#+END_SRC
We will also create a function which turns a list into an ordered binary
tree.
#+BEGIN_SRC haskell :eval never-export
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
(treeFromList (filter (>x) xs))
#+END_SRC
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 a tree where:
- The root is =x=
- Its left subtree is the tree created from members of the list =xs=
which are strictly inferior to =x= and
- the right subtree is the tree created from members of the list =xs=
which are strictly superior to =x=.
#+BEGIN_SRC haskell :eval never-export
main = print $ treeFromList [7,2,4,8]
#+END_SRC
You should obtain the following:
#+BEGIN_EXAMPLE
Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)
#+END_EXAMPLE
This is an informative but quite unpleasant representation of our tree.
02_Hard_Part/30_Trees.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/31_Trees.lhs
Just for fun, let's code a better display for our trees. I simply had
fun making a nice function to display trees in a general way. You can
safely skip this part if you find it too difficult to follow.
We have a few changes to make. We remove the =deriving (Show)= from the
declaration of our =BinTree= type. And it might also be useful to make
our BinTree an instance of (=Eq= and =Ord=) so we will be able to test
equality and compare trees.
#+BEGIN_SRC haskell :eval never-export
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord)
#+END_SRC
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:
#+BEGIN_SRC haskell :eval never-export
instance Show (BinTree a) where
show t = ... -- You declare your function here
#+END_SRC
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.
#+BEGIN_SRC haskell :eval never-export
-- 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"
#+END_SRC
The =treeFromList= method remains identical.
#+BEGIN_SRC haskell :eval never-export
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
(treeFromList (filter (>x) xs))
#+END_SRC
And now, we can play:
#+BEGIN_SRC haskell :eval never-export
main = do
putStrLn "Int binary tree:"
print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
#+END_SRC
#+BEGIN_EXAMPLE
Int binary tree:
< 7
: |--2
: | |--1
: | `--4
: | |--3
: | `--6
: `--8
: `--21
: |--12
: `--23
#+END_EXAMPLE
Now it is far better! The root is shown by starting the line with the
=<= character. And each following line starts with a =:=. But we could
also use another type.
#+BEGIN_SRC haskell :eval never-export
putStrLn "\nString binary tree:"
print $ treeFromList ["foo","bar","baz","gor","yog"]
#+END_SRC
#+BEGIN_EXAMPLE
String binary tree:
< "foo"
: |--"bar"
: | `--"baz"
: `--"gor"
: `--"yog"
#+END_EXAMPLE
As we can test equality and order trees, we can make tree of trees!
#+BEGIN_SRC haskell :eval never-export
putStrLn "\nBinary tree of Char binary trees:"
print ( treeFromList
(map treeFromList ["baz","zara","bar"]))
#+END_SRC
#+BEGIN_EXAMPLE
Binary tree of Char binary trees:
< < 'b'
: : |--'a'
: : `--'z'
: |--< 'b'
: | : |--'a'
: | : `--'r'
: `--< 'z'
: : `--'a'
: : `--'r'
#+END_EXAMPLE
This is why I chose to prefix each line of tree display by =:= (except
for the root).
blogimage("yo_dawg_tree.jpg","Yo Dawg Tree")
#+BEGIN_SRC haskell :eval never-export
putStrLn "\nTree of Binary trees of Char binary trees:"
print $ (treeFromList . map (treeFromList . map treeFromList))
[ ["YO","DAWG"]
, ["I","HEARD"]
, ["I","HEARD"]
, ["YOU","LIKE","TREES"] ]
#+END_SRC
Which is equivalent to
#+BEGIN_SRC haskell :eval never-export
print ( treeFromList (
map treeFromList
[ map treeFromList ["YO","DAWG"]
, map treeFromList ["I","HEARD"]
, map treeFromList ["I","HEARD"]
, map treeFromList ["YOU","LIKE","TREES"] ]))
#+END_SRC
and gives:
#+BEGIN_EXAMPLE
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'
#+END_EXAMPLE
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
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/40_Infinites_Structures.lhs
** Infinite Structures
:PROPERTIES:
:CUSTOM_ID: infinite-structures
:END:
blogimage("escher_infinite_lizards.jpg","Escher")
It is often said that Haskell is /lazy/.
In fact, if you are a bit pedantic, you should say that
[[http://www.haskell.org/haskellwiki/Lazy_vs._non-strict][Haskell is
/non-strict/]]. Laziness is just a common implementation for non-strict
languages.
Then what does "not-strict" mean? From the Haskell wiki:
#+BEGIN_QUOTE
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)=
#+END_QUOTE
For example in Haskell you can do:
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
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
#+BEGIN_EXAMPLE
[1..] ⇔ [1,2,3,4...]
[1,3..] ⇔ [1,3,5,7,9,11...]
#+END_EXAMPLE
and most functions will work with them. Also, there is a built-in
function =take= which is equivalent to our =take'=.
02_Hard_Part/40_Infinites_Structures.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
02_Hard_Part/41_Infinites_Structures.lhs
This code is mostly the same as the previous one.
#+BEGIN_SRC haskell :eval never-export
import Debug.Trace (trace)
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord)
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
-- 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"
#+END_SRC
Suppose we don't mind having an ordered binary tree. Here is an infinite
binary tree:
#+BEGIN_SRC haskell :eval never-export
nullTree = Node 0 nullTree nullTree
#+END_SRC
A complete binary tree where each node is equal to 0. Now I will prove
you can manipulate this object using the following function:
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
See what occurs for this program:
#+BEGIN_SRC haskell :eval never-export
main = print $ treeTakeDepth 4 nullTree
#+END_SRC
This code compiles, runs and stops giving the following result:
#+BEGIN_EXAMPLE
< 0
: |-- 0
: | |-- 0
: | | |-- 0
: | | `-- 0
: | `-- 0
: | |-- 0
: | `-- 0
: `-- 0
: |-- 0
: | |-- 0
: | `-- 0
: `-- 0
: |-- 0
: `-- 0
#+END_EXAMPLE
Just to heat up your neurones a bit more, let's make a slightly more
interesting tree:
#+BEGIN_SRC haskell :eval never-export
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)
#+END_SRC
Another way to create this tree is to use a higher order function. This
function should be similar to =map=, but should work on =BinTree=
instead of list. Here is such a function:
#+BEGIN_SRC haskell :eval never-export
-- 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)
#+END_SRC
/Hint/: I won't talk more about this here. If you are interested in the
generalization of =map= to other data structures, search for functor and
=fmap=.
Our definition is now:
#+BEGIN_SRC haskell :eval never-export
infTreeTwo :: BinTree Int
infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
(treeMap (\x -> x+1) infTreeTwo)
#+END_SRC
Look at the result for
#+BEGIN_SRC haskell :eval never-export
main = print $ treeTakeDepth 4 infTreeTwo
#+END_SRC
#+BEGIN_EXAMPLE
< 0
: |-- -1
: | |-- -2
: | | |-- -3
: | | `-- -1
: | `-- 0
: | |-- -1
: | `-- 1
: `-- 1
: |-- 0
: | |-- -1
: | `-- 1
: `-- 2
: |-- 1
: `-- 3
#+END_EXAMPLE
#+BEGIN_SRC haskell :eval never-export
main = do
print $ treeTakeDepth 4 nullTree
print $ treeTakeDepth 4 infTreeTwo
#+END_SRC
02_Hard_Part/41_Infinites_Structures.lhs
#+BEGIN_HTML
<h2 id="hell-difficulty-part">
#+END_HTML
Hell Difficulty Part
#+BEGIN_HTML
</h2>
#+END_HTML
Congratulations for getting so far! Now, some of the really hardcore
stuff can start.
If you are like me, you should get the functional style. You should also
understand a bit more the advantages of laziness by default. But you
also don't really understand where to start in order 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, the answers might be complex. But they are all very
rewarding.
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/01_IO/01_progressive_io_example.lhs
#+BEGIN_HTML
<h3 id="deal-with-io">
#+END_HTML
Deal With IO
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("magritte_carte_blanche.jpg","Magritte, Carte blanche")
#+BEGIN_QUOTE
%tldr
A typical function doing =IO= looks a lot like an imperative program:
#+BEGIN_EXAMPLE
f :: IO a
f = do
x <- action1
action2 x
y <- action3
action4 x y
#+END_EXAMPLE
- 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 choose. In
particular you cannot use pure functions directly here. To use pure
functions you could do =action2 (purefunction x)= for example.
#+END_QUOTE
In this section, I will explain how to use IO, not how it works. You'll
see how Haskell separates the pure from the impure parts of the program.
Don't stop because you're trying to understand the details of the
syntax. Answers will come in the next section.
What to achieve?
#+BEGIN_QUOTE
Ask a user to enter a list of numbers. Print the sum of the numbers
#+END_QUOTE
#+BEGIN_SRC haskell :eval never-export
toList :: String -> [Integer]
toList input = read ("[" ++ input ++ "]")
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
print $ sum (toList input)
#+END_SRC
It should be straightforward to understand the behavior of this program.
Let's analyze the types in more detail.
#+BEGIN_EXAMPLE
putStrLn :: String -> IO ()
getLine :: IO String
print :: Show a => a -> IO ()
#+END_EXAMPLE
Or more interestingly, we note that each expression in the =do= block
has a type of =IO a=.
#+BEGIN_HTML
<pre>
main = do
putStrLn "Enter ... " :: <span class="high">IO ()</span>
getLine :: <span class="high">IO String</span>
print Something :: <span class="high">IO ()</span>
</pre>
#+END_HTML
We should also pay attention to the effect of the =<-= symbol.
#+BEGIN_EXAMPLE
do
x <- something
#+END_EXAMPLE
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:
#+BEGIN_EXAMPLE
action1 :: IO a
-- in this case, generally a = ()
#+END_EXAMPLE
ou
#+BEGIN_EXAMPLE
value <- action2 -- where
-- action2 :: IO b
-- value :: b
#+END_EXAMPLE
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
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/01_IO/02_progressive_io_example.lhs
Now let's see how this program behaves. For example, what happens if the
user enters something strange? Let's try:
#+BEGIN_EXAMPLE
% runghc 02_progressive_io_example.lhs
Enter a list of numbers (separated by comma):
foo
Prelude.read: no parse
#+END_EXAMPLE
Argh! An evil error message and a crash! Our first improvement will
simply be to answer with a more friendly message.
In order to do this, we must detect that something went wrong. Here is
one way to do this: use the type =Maybe=. This is a very common type in
Haskell.
#+BEGIN_SRC haskell :eval never-export
import Data.Maybe
#+END_SRC
What is this thing? =Maybe= is a type which takes one parameter. Its
definition is:
#+BEGIN_SRC haskell :eval never-export
data Maybe a = Nothing | Just a
#+END_SRC
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=[fn:4], 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=.
#+BEGIN_SRC haskell :eval never-export
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
#+END_SRC
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]=.
#+BEGIN_SRC haskell :eval never-export
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
#+END_SRC
We simply have to test the value in our main function.
#+BEGIN_SRC haskell :eval never-export
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."
#+END_SRC
In case of error, we display a nice error message.
Note that 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 just
say here that =error msg= takes the needed type (here =IO ()=).
One very important thing to note is the type of all the functions
defined so far. There is only one function which contains =IO= in its
type: =main=. This means main is impure. But main uses
=getListFromString= which is pure. So it's clear just by looking at
declared types which functions are pure and which are impure.
Why does purity matter? Among the many advantages, here are three:
- It is far easier to think about pure code than impure code.
- Purity protects you from all the hard-to-reproduce bugs that are due
to side 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 inside
pure functions.
03_Hell/01_IO/02_progressive_io_example.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/01_IO/03_progressive_io_example.lhs
Our next iteration will be to prompt the user again and again until she
enters a valid answer.
We keep the first part:
#+BEGIN_SRC haskell :eval never-export
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 ++ "]"
#+END_SRC
Now we create a function which will ask the user for an list of integers
until the input is right.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
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:
#+BEGIN_QUOTE
«This is an =[Integer]= inside an =IO=»
#+END_QUOTE
If you want to understand the details behind all of this, you'll have to
read the next section. But really, if you just want to /use/ IO just
practice a little and remember to think about the type.
Finally our main function is much simpler:
#+BEGIN_SRC haskell :eval never-export
main :: IO ()
main = do
list <- askUser
print $ sum list
#+END_SRC
We have finished with our introduction to =IO=. This was quite fast.
Here are the main things to remember:
- in the =do= block, each expression must have the type =IO a=. You are
then limited with regard to the range of expressions available. For
example, =getLine=, =print=, =putStrLn=, etc...
- Try to externalize the pure functions as much as possible.
- the =IO a= type means: an IO /action/ which returns an element of type =a=. =IO= represents actions; under the hood, =IO a= is the type of a
function. Read the next section if you are curious.
If you practice a bit, you should be able to /use/ =IO=.
#+BEGIN_QUOTE
/Exercises/:
- Make a program that sums all of its arguments. Hint: use the
function =getArgs=.
#+END_QUOTE
03_Hell/01_IO/03_progressive_io_example.lhs
#+BEGIN_HTML
<h3 id="io-trick-explained">
#+END_HTML
IO trick explained
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("magritte_pipe.jpg","Magritte, ceci n'est pas une pipe")
#+BEGIN_QUOTE
Here is a %tldr for this section.
To separate pure and impure parts, =main= is defined as a function
which modifies the state of the world.
#+BEGIN_EXAMPLE
main :: World -> World
#+END_EXAMPLE
A function is guaranteed to have side effects only if it has this
type. But look at a typical main function:
#+BEGIN_EXAMPLE
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
#+END_EXAMPLE
We have a lot of temporary elements (here =w1=, =w2= and =w3=) which
must be passed on to the next action.
We create a function =bind= or =(>>=)=. With =bind= we don't need
temporary names anymore.
#+BEGIN_EXAMPLE
main =
action1 >>= action2 >>= action3 >>= action4
#+END_EXAMPLE
Bonus: Haskell has syntactical sugar for us:
#+BEGIN_EXAMPLE
main = do
v1 <- action1
v2 <- action2 v1
v3 <- action3 v2
action4 v3
#+END_EXAMPLE
#+END_QUOTE
Why did we use this strange syntax, and what exactly is this =IO= type?
It looks a bit like magic.
For now let's just forget all about the pure parts of our program, and
focus on the impure parts:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
First remark: this looks imperative. 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=, an imperative style
is generally more appropriate.
But you should have noticed that 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. Whether a file exists or not is a difference in the
possible states that the world can take.
In Haskell the current state of the world is not hidden. Rather, it is
/explicitly/ said that =main= is a function that /potentially/ changes
the state of the world. Its type is then something like:
#+BEGIN_SRC haskell :eval never-export
main :: World -> World
#+END_SRC
Not all functions may access this variable. Those which have access to
this variable are impure. Functions to which the world variable isn't
provided are pure[fn:5].
Haskell considers the state of the world as an input variable to =main=.
But the real type of main is closer to this one[fn:6]:
#+BEGIN_SRC haskell :eval never-export
main :: World -> ((),World)
#+END_SRC
The =()= type is the unit type. Nothing to see here.
Now let's rewrite our main function with this in mind:
#+BEGIN_SRC haskell :eval never-export
main w0 =
let (list,w1) = askUser w0 in
let (x,w2) = print (sum list,w1) in
x
#+END_SRC
First, we note that all functions which have side effects must have the
type:
#+BEGIN_SRC haskell :eval never-export
World -> (a,World)
#+END_SRC
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're working in a pure part of the 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.
This trick works like a charm. 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:
#+BEGIN_SRC haskell :eval never-export
askUser :: World -> ([Integer],World)
#+END_SRC
Before:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
After:
#+BEGIN_SRC haskell :eval never-export
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)
#+END_SRC
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:
#+BEGIN_SRC haskell :eval never-export
let (y,w') = action x w in
#+END_SRC
Even if for some lines the first =x= argument isn't needed. The output
type is a couple, =(answer, newWorldValue)=. Each function =f= must have
a type similar to:
#+BEGIN_SRC haskell :eval never-export
f :: World -> (a,World)
#+END_SRC
Not only this, but we can also note that we always follow the same usage
pattern:
#+BEGIN_SRC haskell :eval never-export
let (y,w1) = action1 w0 in
let (z,w2) = action2 w1 in
let (t,w3) = action3 w2 in
...
#+END_SRC
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:
#+BEGIN_SRC haskell :eval never-export
let (_,w1) = action1 x w0 in
let (z,w2) = action2 w1 in
let (_,w3) = action3 z w2 in
...
#+END_SRC
With, of course: =actionN w :: (World) -> (a,World)=.
#+BEGIN_QUOTE
IMPORTANT: there are only two important patterns to consider:
#+BEGIN_EXAMPLE
let (x,w1) = action1 w0 in
let (y,w2) = action2 x w1 in
#+END_EXAMPLE
and
#+BEGIN_EXAMPLE
let (_,w1) = action1 w0 in
let (y,w2) = action2 w1 in
#+END_EXAMPLE
#+END_QUOTE
leftblogimage("jocker_pencil_trick.jpg","Jocker pencil trick")
Now, we will do a magic trick. We will make the temporary world symbols
"disappear". We will =bind= the two lines. Let's define the =bind=
function. Its type is quite intimidating at first:
#+BEGIN_SRC haskell :eval never-export
bind :: (World -> (a,World))
-> (a -> (World -> (b,World)))
-> (World -> (b,World))
#+END_SRC
But remember that =(World -> (a,World))= is the type for an IO action.
Now let's rename it for clarity:
#+BEGIN_SRC haskell :eval never-export
type IO a = World -> (a, World)
#+END_SRC
Some examples of functions:
#+BEGIN_SRC haskell :eval never-export
getLine :: IO String
print :: Show a => a -> IO ()
#+END_SRC
=getLine= is an IO action which takes world as a parameter and returns a
couple =(String, World)=. This 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 that it changes the state of the world,
but doesn't yield any more data.
This new =IO a= type helps us simplify the type of =bind=:
#+BEGIN_SRC haskell :eval never-export
bind :: IO a
-> (a -> IO b)
-> IO b
#+END_SRC
It says that =bind= takes two IO actions as parameters and returns
another IO action.
Now, remember the /important/ patterns. The first was:
#+BEGIN_SRC haskell :eval never-export
pattern1 w0 =
let (x,w1) = action1 w0 in
let (y,w2) = action2 x w1 in
(y,w2)
#+END_SRC
Look at the types:
#+BEGIN_SRC haskell :eval never-export
action1 :: IO a
action2 :: a -> IO b
pattern1 :: IO b
#+END_SRC
Doesn't it seem familiar?
#+BEGIN_SRC haskell :eval never-export
(bind action1 action2) w0 =
let (x, w1) = action1 w0
(y, w2) = action2 x w1
in (y, w2)
#+END_SRC
The idea is to hide the World argument with this function. Let's go: As
an example imagine if we wanted to simulate:
#+BEGIN_SRC haskell :eval never-export
let (line1, w1) = getLine w0 in
let ((), w2) = print line1 in
((), w2)
#+END_SRC
Now, using the =bind= function:
#+BEGIN_SRC haskell :eval never-export
(res, w2) = (bind getLine print) w0
#+END_SRC
As print is of type =Show a => a -> (World -> ((), World))=, we know
=res = ()= (=unit= type). If you didn't see what was magic here, let's
try with three lines this time.
#+BEGIN_SRC haskell :eval never-export
let (line1,w1) = getLine w0 in
let (line2,w2) = getLine w1 in
let ((),w3) = print (line1 ++ line2) in
((),w3)
#+END_SRC
Which is equivalent to:
#+BEGIN_SRC haskell :eval never-export
(res,w3) = (bind getLine (\line1 ->
(bind getLine (\line2 ->
print (line1 ++ line2))))) w0
#+END_SRC
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=
#+BEGIN_SRC haskell :eval never-export
(res,w3) = (getLine >>=
(\line1 -> getLine >>=
(\line2 -> print (line1 ++ line2)))) w0
#+END_SRC
fr; Haskell a confectionné du sucre syntaxique pour vous : Ho Ho Ho!
Merry Christmas Everyone! Haskell has made syntactical sugar for us:
#+BEGIN_SRC haskell :eval never-export
do
x <- action1
y <- action2
z <- action3
...
#+END_SRC
Is replaced by:
#+BEGIN_SRC haskell :eval never-export
action1 >>= (\x ->
action2 >>= (\y ->
action3 >>= (\z ->
...
)))
#+END_SRC
Note that you can use =x= in =action2= and =x= and =y= in =action3=.
But what about the lines not using the =<-=? Easy, another function
=blindBind=:
#+BEGIN_SRC haskell :eval never-export
blindBind :: IO a -> IO b -> IO b
blindBind action1 action2 w0 =
bind action (\_ -> action2) w0
#+END_SRC
I didn't simplify this definition for the purposes of clarity. Of
course, we can use a better notation: we'll use the =(>>)= operator.
And
#+BEGIN_SRC haskell :eval never-export
do
action1
action2
action3
#+END_SRC
Is transformed into
#+BEGIN_SRC haskell :eval never-export
action1 >>
action2 >>
action3
#+END_SRC
Also, another function is quite useful.
#+BEGIN_SRC haskell :eval never-export
putInIO :: a -> IO a
putInIO x = IO (\w -> (x,w))
#+END_SRC
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.
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/01_IO/21_Detailled_IO.lhs
To finish, let's translate our example:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
Is translated into:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
You can compile this code to verify that it works.
Imagine what it would look like without the =(>>)= and =(>>=)=.
03_Hell/01_IO/21_Detailled_IO.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/02_Monads/10_Monads.lhs
#+BEGIN_HTML
<h3 id="monads">
#+END_HTML
Monads
#+BEGIN_HTML
</h3>
#+END_HTML
blogimage("dali_reve.jpg","Dali, reve. It represents a weapon out of the
mouth of a tiger, itself out of the mouth of another tiger, itself out
of the mouth of a fish itself out of a grenade. I could have choosen a
picture of the Human centipede as it is a very good representation of
what a monad really is. But just to think about it, I find this
disgusting and that wasn't the purpose of this document.")
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 a coding pattern which will ease the flow of
your code.
#+BEGIN_QUOTE
*Important remarks*:
- Monad are not necessarily about effects! There are a lot of /pure/
monads.
- Monad are more about sequencing
#+END_QUOTE
In Haskell, =Monad= is a type class. To be an instance of this type
class, you must provide the functions =(>>=)= and =return=. The function
=(>>)= is derived from =(>>=)=. Here is how the type class =Monad= is
declared (basically):
#+BEGIN_SRC haskell :eval never-export
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 reasons
fail :: String -> m a
fail = error
#+END_SRC
#+BEGIN_QUOTE
Remarks:
- the keyword =class= is not your friend. A Haskell class is /not/ a
class of the kind you will find in object-oriented programming. A
Haskell class has a lot of similarities with Java interfaces. A
better word would have been =typeclass=, since 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:
#+END_QUOTE
#+BEGIN_QUOTE
#+BEGIN_EXAMPLE
return a >>= k == k a
m >>= return == m
m >>= (\x -> k x >>= h) == (m >>= k) >>= h
#+END_EXAMPLE
#+END_QUOTE
#+BEGIN_HTML
<h4 id="maybe-monad">
#+END_HTML
Maybe is a monad
#+BEGIN_HTML
</h4>
#+END_HTML
There are a lot of different types that are instances of =Monad=. One of
the easiest to describe is =Maybe=. If you have a sequence of =Maybe=
values, you can use monads to manipulate them. It is particularly useful
to remove very deep =if..then..else..= constructions.
Imagine a complex bank operation. You are eligible to gain about 700€
only if you can afford to follow a list of operations without your
balance dipping below zero.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
03_Hell/02_Monads/10_Monads.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/02_Monads/11_Monads.lhs
Now, let's make it better using Maybe and the fact that it is a Monad
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
03_Hell/02_Monads/11_Monads.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/02_Monads/12_Monads.lhs
Not bad, but we can make it even better:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
We have proven that Monads are a good way to make our code more elegant.
Note this idea of code organization, in particular for =Maybe= can be
used in most imperative languages. In fact, this is the kind of
construction we make naturally.
#+BEGIN_QUOTE
An important remark:
The first element in the sequence being evaluated to =Nothing= will
stop the complete evaluation. This means you don't execute all lines.
You get this for free, thanks to laziness.
#+END_QUOTE
You could also replay these example with the definition of =(>>=)= for
=Maybe= in mind:
#+BEGIN_SRC haskell :eval never-export
instance Monad Maybe where
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing
(Just x) >>= f = f x
return x = Just x
#+END_SRC
The =Maybe= monad proved to be useful while being a very simple example.
We saw the utility of the =IO= monad. But now for a cooler example,
lists.
03_Hell/02_Monads/12_Monads.lhs
#+BEGIN_HTML
<hr/>
#+END_HTML
03_Hell/02_Monads/13_Monads.lhs
#+BEGIN_HTML
<h4 id="the-list-monad">
#+END_HTML
The list monad
#+BEGIN_HTML
</h4>
#+END_HTML
blogimage("golconde.jpg","Golconde de Magritte")
The list monad helps us to simulate non-deterministic computations. Here
we go:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
MA. GIC. :
#+BEGIN_EXAMPLE
[(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)]
#+END_EXAMPLE
For the list monad, there is also this syntactic sugar:
#+BEGIN_SRC haskell :eval never-export
print $ [ (x,y,z) | x <- allCases,
y <- allCases,
z <- allCases,
4*x + 2*y < z ]
#+END_SRC
I won't list all the monads, since there are many of them. Using monads
simplifies the manipulation of several notions in pure languages. In
particular, monads 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
monads[fn:7]!
03_Hell/02_Monads/13_Monads.lhs
#+BEGIN_HTML
<h2 id="appendix">
#+END_HTML
Appendix
#+BEGIN_HTML
</h2>
#+END_HTML
This section is not so much about learning Haskell. It is just here to
discuss some details further.
#+BEGIN_HTML
<hr/>
#+END_HTML
04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
#+BEGIN_HTML
<h3 id="more-on-infinite-tree">
#+END_HTML
More on Infinite Tree
#+BEGIN_HTML
</h3>
#+END_HTML
In the section [[#infinite-structures][Infinite Structures]] we saw some
simple constructions. Unfortunately we removed two properties from 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 it but we'll discuss how to keep it as much as
possible.
This code is mostly the same as the one in the [[#trees][tree section]].
#+BEGIN_SRC haskell :eval never-export
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"
#+END_SRC
Our first step is to create some pseudo-random number list:
#+BEGIN_SRC haskell :eval never-export
shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
#+END_SRC
Just as a reminder, here is the definition of =treeFromList=
#+BEGIN_SRC haskell :eval never-export
treeFromList :: (Ord a) => [a] -> BinTree a
treeFromList [] = Empty
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
(treeFromList (filter (>x) xs))
#+END_SRC
and =treeTakeDepth=:
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
See the result of:
#+BEGIN_SRC haskell :eval never-export
main = do
putStrLn "take 10 shuffle"
print $ take 10 shuffle
putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
print $ treeTakeDepth 4 (treeFromList shuffle)
#+END_SRC
#+BEGIN_EXAMPLE
% 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
#+END_EXAMPLE
Yay! It ends! Beware though, it will only work if you always have
something to put into a branch.
For example
#+BEGIN_SRC haskell :eval never-export
treeTakeDepth 4 (treeFromList [1..])
#+END_SRC
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
#+BEGIN_HTML
<hr/>
#+END_HTML
04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
This code is mostly the same as the preceding one.
#+BEGIN_SRC haskell :eval never-export
import Debug.Trace (trace)
import Data.List
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord)
#+END_SRC
#+BEGIN_SRC haskell :eval never-export
-- 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
#+END_SRC
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.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
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:
#+BEGIN_QUOTE
Any element of the left (resp. right) branch must all be strictly
inferior (resp. superior) to the label of the root.
#+END_QUOTE
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=.
#+BEGIN_SRC haskell :eval never-export
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
#+END_SRC
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.
#+BEGIN_SRC haskell :eval never-export
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)
#+END_SRC
Now run the program and be happy:
#+BEGIN_SRC haskell :eval never-export
main = do
putStrLn "take 10 shuffle"
print $ take 10 shuffle
putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
print $ treeTakeDepth 8 (treeFromList $ shuffle)
#+END_SRC
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:
#+BEGIN_HTML
<pre>
safefilter' f l = if filter f (take 10000 l) == []
then []
else filter f l
</pre>
#+END_HTML
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. Disclaimer, this is
only a conjecture.
#+BEGIN_SRC haskell :eval never-export
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 = ???
#+END_SRC
04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
** Thanks
:PROPERTIES:
:CUSTOM_ID: thanks
:END:
Thanks to [[http://reddit.com/r/haskell][=/r/haskell=]] and
[[http://reddit.com/r/programming][=/r/programming=]]. Your comment were
most than welcome.
Particularly, I want to thank [[https://github.com/Emm][Emm]] a thousand
times for the time he spent on correcting my English. Thank you man.
[fn:1] Even if most recent languages try to hide them, they are present.
[fn:2] I know I'm cheating. But I will talk about non-strictness later.
[fn:3] For the brave, a more complete explanation of pattern matching
can be found
[[http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html][here]].
[fn:4] Which is itself very similar to the javascript =eval= function,
that is applied to a string containing JSON.
[fn:5] There are some /unsafe/ exceptions to this rule. But you
shouldn't see such use in a real application except maybe for
debugging purposes.
[fn:6] For the curious ones, the real type is =data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}=.
All the =#= has to do with optimisation and I swapped the fields
in my example. But this is the basic idea.
[fn:7] Well, you'll certainly need to practice a bit to get used to them
and to understand when you can use them and create your own. But
you already made a big step in this direction.