3773 lines
100 KiB
Org Mode
3773 lines
100 KiB
Org Mode
#+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></span> :load 11_Functions.lhs
|
||
<span class="low">[1 of 1] Compiling Main ( 11_Functions.lhs, interpreted )
|
||
Ok, modules loaded: Main.
|
||
*Main></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.
|