wip
This commit is contained in:
parent
383b69bc75
commit
7784f02483
17 changed files with 400 additions and 371 deletions
|
@ -213,12 +213,6 @@ figure, .figure {
|
||||||
.notes {
|
.notes {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
}
|
}
|
||||||
.notes::before {
|
|
||||||
content: "☞";
|
|
||||||
float: left;
|
|
||||||
display: inline-block;
|
|
||||||
width: 1.5em;
|
|
||||||
}
|
|
||||||
.underline {
|
.underline {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
@ -376,12 +370,21 @@ pre::after,pre::before,hr:after,
|
||||||
nav a, nav a:visited, .main nav a,.main nav a:visited {
|
nav a, nav a:visited, .main nav a,.main nav a:visited {
|
||||||
color: var(--fg2);
|
color: var(--fg2);
|
||||||
}
|
}
|
||||||
#labels label:hover, a:hover, a:active, a:focus, .main a:hover,.main a:active,.main a:focus,
|
#labels label:hover,
|
||||||
nav a:focus, nav a:hover, .main nav a:focus,.main nav a:hover {
|
a:hover,
|
||||||
|
a:hover *,
|
||||||
|
.main a:hover,
|
||||||
|
.main a:hover *,
|
||||||
|
nav a:hover,
|
||||||
|
.main nav a:hover {
|
||||||
color: var(--l-fg);
|
color: var(--l-fg);
|
||||||
background: var(--l-bg);
|
background: var(--l-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abbr { border-bottom: dashed 1px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
thead, .main thead, tr:hover, .main tr:hover {
|
thead, .main thead, tr:hover, .main tr:hover {
|
||||||
background: var(--rbg);
|
background: var(--rbg);
|
||||||
color: var(--rfg);
|
color: var(--rfg);
|
||||||
|
@ -428,6 +431,7 @@ blockquote:after, .main blockquote:after {
|
||||||
.notes, .main .notes {
|
.notes, .main .notes {
|
||||||
background: var(--rbg);
|
background: var(--rbg);
|
||||||
color: var(--rfg);
|
color: var(--rfg);
|
||||||
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
/* ---- SYNTAX HIGHLIGHTING ---- */
|
/* ---- SYNTAX HIGHLIGHTING ---- */
|
||||||
.org-rainbow-delimiters-depth-1, .org-rainbow-delimiters-depth-9,
|
.org-rainbow-delimiters-depth-1, .org-rainbow-delimiters-depth-9,
|
||||||
|
|
10
src/posts/0010-Haskell-Now/evenSum_v1.hs
Normal file
10
src/posts/0010-Haskell-Now/evenSum_v1.hs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- 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
|
6
src/posts/0010-Haskell-Now/evenSum_v10.hs
Normal file
6
src/posts/0010-Haskell-Now/evenSum_v10.hs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-- Version 10
|
||||||
|
import Data.List (foldl')
|
||||||
|
sum' :: (Num a) => [a] -> a
|
||||||
|
sum' = foldl' (+) 0
|
||||||
|
evenSum :: Integral a => [a] -> a
|
||||||
|
evenSum = sum' . (filter even)
|
11
src/posts/0010-Haskell-Now/evenSum_v2.hs
Normal file
11
src/posts/0010-Haskell-Now/evenSum_v2.hs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
-- 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
|
8
src/posts/0010-Haskell-Now/evenSum_v3.hs
Normal file
8
src/posts/0010-Haskell-Now/evenSum_v3.hs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
-- 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
|
9
src/posts/0010-Haskell-Now/evenSum_v4.hs
Normal file
9
src/posts/0010-Haskell-Now/evenSum_v4.hs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
-- 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
|
5
src/posts/0010-Haskell-Now/evenSum_v5.hs
Normal file
5
src/posts/0010-Haskell-Now/evenSum_v5.hs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-- Version 5
|
||||||
|
evenSum l = mysum 0 (filter even l)
|
||||||
|
where
|
||||||
|
mysum n [] = n
|
||||||
|
mysum n (x:xs) = mysum (n+x) xs
|
6
src/posts/0010-Haskell-Now/evenSum_v6.hs
Normal file
6
src/posts/0010-Haskell-Now/evenSum_v6.hs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-- 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
|
5
src/posts/0010-Haskell-Now/evenSum_v7.hs
Normal file
5
src/posts/0010-Haskell-Now/evenSum_v7.hs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-- 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)
|
4
src/posts/0010-Haskell-Now/evenSum_v8.hs
Normal file
4
src/posts/0010-Haskell-Now/evenSum_v8.hs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
-- Version 8
|
||||||
|
import Data.List (foldl')
|
||||||
|
evenSum :: Integral a => [a] -> a
|
||||||
|
evenSum l = foldl' (+) 0 (filter even l)
|
4
src/posts/0010-Haskell-Now/evenSum_v9.hs
Normal file
4
src/posts/0010-Haskell-Now/evenSum_v9.hs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
-- Version 9
|
||||||
|
import Data.List (foldl')
|
||||||
|
evenSum :: Integral a => [a] -> a
|
||||||
|
evenSum = (foldl' (+) 0) . (filter even)
|
25
src/posts/0010-Haskell-Now/functions.hs
Normal file
25
src/posts/0010-Haskell-Now/functions.hs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
square :: Num a => a -> a
|
||||||
|
square x = x^2
|
||||||
|
|
||||||
|
square' x = (^) x 2
|
||||||
|
|
||||||
|
square'' x = (^2) x
|
||||||
|
|
||||||
|
square''' = (^2)
|
||||||
|
|
||||||
|
absolute :: (Ord a, Num a) => a -> a
|
||||||
|
absolute x = if x >= 0 then x else -x
|
||||||
|
|
||||||
|
absolute' x
|
||||||
|
| x >= 0 = x
|
||||||
|
| otherwise = -x
|
||||||
|
|
||||||
|
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)
|
|
@ -230,16 +230,32 @@ Congratulations you should be ready to start now.
|
||||||
|
|
||||||
#+begin_notes
|
#+begin_notes
|
||||||
- There are multiple ways to install Haskell and I don't think there is a
|
- There are multiple ways to install Haskell and I don't think there is a
|
||||||
full consensus between developer about what is the best method. If you
|
full consensus between developer about what is the best method.
|
||||||
whish to use another method take a look at [[http://haskell.org][haskell.org]].
|
If you whish to use another method take a look at [[http://haskell.org][haskell.org]].
|
||||||
- This install method is only suitable for using as a playground and
|
- This install method is only suitable for using as a playground and I
|
||||||
perfect for this tutorial. I don't think it is suitable for serious
|
think perfectly adapted to run code example from this article.
|
||||||
development.
|
I do not recommend it for serious development.
|
||||||
- =nix= is a generic package manager and goes beyond Haskell. One great
|
- =nix= is a generic package manager and goes beyond Haskell.
|
||||||
good point is that it does not only manage Haskell packages but really a
|
One great good point is that it does not only manage Haskell packages but
|
||||||
lot of other kind of packages. This can be quite helpful if you need to
|
really a lot of other kind of packages.
|
||||||
depends on a Haskell package that itself depends on a system library, for
|
This can be quite helpful if you need to depends on a Haskell package that
|
||||||
example =ncurses=.
|
itself depends on a system library, for example =ncurses=.
|
||||||
|
- I use [[http://nixos.org/nix][=nix=]] for other projects unrelated to Haskell.
|
||||||
|
For example, I use the nix-shell bang pattern for shell script for which
|
||||||
|
I can assume the executable I want are present.
|
||||||
|
#+end_notes
|
||||||
|
|
||||||
|
#+begin_notes
|
||||||
|
*BONUS*: use [[https://direnv.net][=direnv=]]
|
||||||
|
|
||||||
|
#+begin_src
|
||||||
|
~ cd hsenv
|
||||||
|
~ echo "use nix" > .envrc
|
||||||
|
~ direnv allow
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Now each time you'll cd into your hsenv directory you'll get the
|
||||||
|
environment set for you.
|
||||||
#+end_notes
|
#+end_notes
|
||||||
|
|
||||||
** Don't be afraid
|
** Don't be afraid
|
||||||
|
@ -338,7 +354,7 @@ of new things.
|
||||||
Hopefully many of these new concepts will help you to program even in
|
Hopefully many of these new concepts will help you to program even in
|
||||||
imperative languages.
|
imperative languages.
|
||||||
|
|
||||||
/Smart Static Typing/
|
/Advanced Static Typing/
|
||||||
|
|
||||||
Instead of being in your way like in =C=, =C++= or =Java=, the type system
|
Instead of being in your way like in =C=, =C++= or =Java=, the type system
|
||||||
is here to help you.
|
is here to help you.
|
||||||
|
@ -455,7 +471,7 @@ We declare the type using =::=
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
[nix-shell:~/tmp/hsenv]$ runghc basic.hs
|
[nix-shell:~/hsenv]$ runghc basic.hs
|
||||||
13
|
13
|
||||||
#+END_EXAMPLE
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
@ -472,7 +488,7 @@ Now try
|
||||||
You should get this error:
|
You should get this error:
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
[nix-shell:~/tmp/hsenv]$ runghc error_basic.hs
|
[nix-shell:~/hsenv]$ runghc error_basic.hs
|
||||||
|
|
||||||
error_basic.hs:4:17: error:
|
error_basic.hs:4:17: error:
|
||||||
• No instance for (Fractional Int) arising from the literal ‘2.3’
|
• No instance for (Fractional Int) arising from the literal ‘2.3’
|
||||||
|
@ -497,7 +513,7 @@ infer the most general type for us:
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+begin_example
|
#+begin_example
|
||||||
[nix-shell:~/tmp/hsenv]$ runghc float_basic.hs
|
[nix-shell:~/hsenv]$ runghc float_basic.hs
|
||||||
22.93
|
22.93
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
|
@ -823,7 +839,7 @@ But it is considered a good practice to do so.
|
||||||
|
|
||||||
/Infix notation/
|
/Infix notation/
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
square :: Num a => a -> a
|
square :: Num a => a -> a
|
||||||
square x = x^2
|
square x = x^2
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -832,7 +848,7 @@ Note =^= uses infix notation.
|
||||||
For each infix operator there its associated prefix notation.
|
For each infix operator there its associated prefix notation.
|
||||||
You just have to put it inside parenthesis.
|
You just have to put it inside parenthesis.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
square' x = (^) x 2
|
square' x = (^) x 2
|
||||||
|
|
||||||
square'' x = (^2) x
|
square'' x = (^2) x
|
||||||
|
@ -841,7 +857,7 @@ You just have to put it inside parenthesis.
|
||||||
We can remove =x= in the left and right side!
|
We can remove =x= in the left and right side!
|
||||||
It's called η-reduction.
|
It's called η-reduction.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
square''' = (^2)
|
square''' = (^2)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
@ -852,11 +868,18 @@ Here:
|
||||||
=square= ⇔ =square'= ⇔ =square''= ⇔ =square'''=
|
=square= ⇔ =square'= ⇔ =square''= ⇔ =square'''=
|
||||||
#+END_QUOTE
|
#+END_QUOTE
|
||||||
|
|
||||||
|
Note for each prefix notation you can transform it to infix notation with
|
||||||
|
=`= like this:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
foo x y ↔ x `foo` y
|
||||||
|
#+end_example
|
||||||
|
|
||||||
/Tests/
|
/Tests/
|
||||||
|
|
||||||
An implementation of the absolute function.
|
An implementation of the absolute function.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
absolute :: (Ord a, Num a) => a -> a
|
absolute :: (Ord a, Num a) => a -> a
|
||||||
absolute x = if x >= 0 then x else -x
|
absolute x = if x >= 0 then x else -x
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -867,7 +890,7 @@ You cannot forget the =else=.
|
||||||
|
|
||||||
Another equivalent version:
|
Another equivalent version:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
absolute' x
|
absolute' x
|
||||||
| x >= 0 = x
|
| x >= 0 = x
|
||||||
| otherwise = -x
|
| otherwise = -x
|
||||||
|
@ -878,7 +901,8 @@ Notation warning: indentation is /important/ in Haskell.
|
||||||
Like in Python, bad indentation can break your code!
|
Like in Python, bad indentation can break your code!
|
||||||
#+END_QUOTE
|
#+END_QUOTE
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(functions.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle functions.hs
|
||||||
main = do
|
main = do
|
||||||
print $ square 10
|
print $ square 10
|
||||||
print $ square' 10
|
print $ square' 10
|
||||||
|
@ -890,13 +914,22 @@ Like in Python, bad indentation can break your code!
|
||||||
print $ absolute' (-10)
|
print $ absolute' (-10)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
* Difficulty: Normal
|
#+begin_example
|
||||||
|
~/t/hsenv> runghc functions.hs
|
||||||
|
100
|
||||||
|
100
|
||||||
|
100
|
||||||
|
100
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Difficulty: First steps
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: hard-part
|
:CUSTOM_ID: difficulty--first-steps
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
The hard part can now begin.
|
|
||||||
|
|
||||||
** Functional style
|
** Functional style
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: functional-style
|
:CUSTOM_ID: functional-style
|
||||||
|
@ -920,7 +953,7 @@ example: =[1,2,3,4,5] ⇒ 2 + 4 ⇒ 6=
|
||||||
#+END_QUOTE
|
#+END_QUOTE
|
||||||
|
|
||||||
To show differences between functional and imperative approaches, I'll
|
To show differences between functional and imperative approaches, I'll
|
||||||
start by providing an imperative solution (in JavaScript):
|
start by providing an imperative solution (in javascript):
|
||||||
|
|
||||||
#+BEGIN_SRC javascript
|
#+BEGIN_SRC javascript
|
||||||
function evenSum(list) {
|
function evenSum(list) {
|
||||||
|
@ -1010,12 +1043,11 @@ Note that for any non empty list =l=, =l ⇔ (head l):(tail l)=
|
||||||
The first Haskell solution.
|
The first Haskell solution.
|
||||||
The function =evenSum= returns the sum of all even numbers in a list:
|
The function =evenSum= returns the sum of all even numbers in a list:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v1.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v1.hs
|
||||||
-- Version 1
|
-- Version 1
|
||||||
evenSum :: [Integer] -> Integer
|
evenSum :: [Integer] -> Integer
|
||||||
|
|
||||||
evenSum l = accumSum 0 l
|
evenSum l = accumSum 0 l
|
||||||
|
|
||||||
accumSum n l = if l == []
|
accumSum n l = if l == []
|
||||||
then n
|
then n
|
||||||
else let x = head l
|
else let x = head l
|
||||||
|
@ -1027,39 +1059,36 @@ The function =evenSum= returns the sum of all even numbers in a list:
|
||||||
|
|
||||||
To test a function you can use =ghci=:
|
To test a function you can use =ghci=:
|
||||||
|
|
||||||
#+BEGIN_HTML
|
#+begin_example
|
||||||
% ghci
|
~/t/hsenv> ghci
|
||||||
GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help
|
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
|
||||||
Loading package ghc-prim ... linking ... done.
|
Prelude> :l evenSum_v1.hs
|
||||||
Loading package integer-gmp ... linking ... done.
|
[1 of 1] Compiling Main ( evenSum_v1.hs, interpreted )
|
||||||
Loading package base ... linking ... done.
|
Ok, one module loaded.
|
||||||
Prelude> :load 11_Functions.lhs
|
*Main> evenSum [1..5]
|
||||||
[1 of 1] Compiling Main ( 11_Functions.lhs, interpreted )
|
6
|
||||||
Ok, modules loaded: Main.
|
#+end_example
|
||||||
*Main> evenSum [1..5]
|
|
||||||
6
|
|
||||||
#+END_HTML
|
|
||||||
|
|
||||||
Here is an example of execution[fn:2]:
|
Here is an example of execution[fn:2]:
|
||||||
|
|
||||||
#+BEGIN_HTML
|
#+begin_example
|
||||||
*Main> evenSum [1..5]
|
*Main> evenSum [1..5]
|
||||||
accumSum 0 [1,2,3,4,5]
|
accumSum 0 [1,2,3,4,5]
|
||||||
1 is odd
|
1 is odd
|
||||||
accumSum 0 [2,3,4,5]
|
accumSum 0 [2,3,4,5]
|
||||||
2 is even
|
2 is even
|
||||||
accumSum (0+2) [3,4,5]
|
accumSum (0+2) [3,4,5]
|
||||||
3 is odd
|
3 is odd
|
||||||
accumSum (0+2) [4,5]
|
accumSum (0+2) [4,5]
|
||||||
2 is even
|
2 is even
|
||||||
accumSum (0+2+4) [5]
|
accumSum (0+2+4) [5]
|
||||||
5 is odd
|
5 is odd
|
||||||
accumSum (0+2+4) []
|
accumSum (0+2+4) []
|
||||||
l == []
|
l == []
|
||||||
0+2+4
|
0+2+4
|
||||||
0+6
|
0+6
|
||||||
6
|
6
|
||||||
#+END_HTML
|
#+end_example
|
||||||
|
|
||||||
Coming from an imperative language all should seem right.
|
Coming from an imperative language all should seem right.
|
||||||
In fact, many things can be improved here.
|
In fact, many things can be improved here.
|
||||||
|
@ -1069,17 +1098,14 @@ First, we can generalize the type.
|
||||||
evenSum :: Integral a => [a] -> a
|
evenSum :: Integral a => [a] -> a
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = do print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
Next, we can use sub functions using =where= or =let=.
|
Next, we can use sub functions using =where= or =let=.
|
||||||
This way our =accumSum= function won't pollute the namespace of our module.
|
This way our =accumSum= function will not pollute the namespace of our
|
||||||
|
module.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v2.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v2.hs
|
||||||
-- Version 2
|
-- Version 2
|
||||||
evenSum :: Integral a => [a] -> a
|
evenSum :: Integral a => [a] -> a
|
||||||
|
|
||||||
evenSum l = accumSum 0 l
|
evenSum l = accumSum 0 l
|
||||||
where accumSum n l =
|
where accumSum n l =
|
||||||
if l == []
|
if l == []
|
||||||
|
@ -1091,15 +1117,10 @@ This way our =accumSum= function won't pollute the namespace of our module.
|
||||||
else accumSum n xs
|
else accumSum n xs
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
Next, we can use pattern matching.
|
Next, we can use pattern matching.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v3.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v3.hs
|
||||||
-- Version 3
|
-- Version 3
|
||||||
evenSum l = accumSum 0 l
|
evenSum l = accumSum 0 l
|
||||||
where
|
where
|
||||||
|
@ -1143,10 +1164,6 @@ with
|
||||||
This is a very useful feature.
|
This is a very useful feature.
|
||||||
It makes our code both terser and easier to read.
|
It makes our code both terser and easier to read.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
In Haskell you can simplify function definitions by η-reducing them.
|
In Haskell you can simplify function definitions by η-reducing them.
|
||||||
For example, instead of writing:
|
For example, instead of writing:
|
||||||
|
|
||||||
|
@ -1157,15 +1174,15 @@ For example, instead of writing:
|
||||||
you can simply write
|
you can simply write
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell
|
||||||
f = some expression
|
f = (some expression)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
We use this method to remove the =l=:
|
We use this method to remove the =l=:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v4.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v4.hs
|
||||||
-- Version 4
|
-- Version 4
|
||||||
evenSum :: Integral a => [a] -> a
|
evenSum :: Integral a => [a] -> a
|
||||||
|
|
||||||
evenSum = accumSum 0
|
evenSum = accumSum 0
|
||||||
where
|
where
|
||||||
accumSum n [] = n
|
accumSum n [] = n
|
||||||
|
@ -1175,10 +1192,6 @@ We use this method to remove the =l=:
|
||||||
else accumSum n xs
|
else accumSum n xs
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
*** Higher Order Functions
|
*** Higher Order Functions
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: higher-order-functions
|
:CUSTOM_ID: higher-order-functions
|
||||||
|
@ -1201,7 +1214,8 @@ Here are some examples:
|
||||||
|
|
||||||
Let's proceed by small steps.
|
Let's proceed by small steps.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v5.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v5.hs
|
||||||
-- Version 5
|
-- Version 5
|
||||||
evenSum l = mysum 0 (filter even l)
|
evenSum l = mysum 0 (filter even l)
|
||||||
where
|
where
|
||||||
|
@ -1218,7 +1232,7 @@ where
|
||||||
The function =filter= takes a function of type (=a -> Bool=) and a list of
|
The function =filter= takes a function of type (=a -> Bool=) and a list of
|
||||||
type =[a]=.
|
type =[a]=.
|
||||||
It returns a list containing only elements for which the function returned
|
It returns a list containing only elements for which the function returned
|
||||||
=true=.
|
=True=.
|
||||||
|
|
||||||
Our next step is to use another technique to accomplish the same thing as a
|
Our next step is to use another technique to accomplish the same thing as a
|
||||||
loop.
|
loop.
|
||||||
|
@ -1260,7 +1274,8 @@ follow the code as if =foldl= and =foldl'= were identical.
|
||||||
|
|
||||||
Now our new version of =evenSum= becomes:
|
Now our new version of =evenSum= becomes:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v6.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v6.hs
|
||||||
-- Version 6
|
-- Version 6
|
||||||
-- foldl' isn't accessible by default
|
-- foldl' isn't accessible by default
|
||||||
-- we need to import it from the module Data.List
|
-- we need to import it from the module Data.List
|
||||||
|
@ -1272,7 +1287,8 @@ Now our new version of =evenSum= becomes:
|
||||||
We can also simplify this by using directly a lambda notation.
|
We can also simplify this by using directly a lambda notation.
|
||||||
This way we don't have to create the temporary name =mysum=.
|
This way we don't have to create the temporary name =mysum=.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v7.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v7.hs
|
||||||
-- Version 7
|
-- Version 7
|
||||||
-- Generally it is considered a good practice
|
-- Generally it is considered a good practice
|
||||||
-- to import only the necessary function(s)
|
-- to import only the necessary function(s)
|
||||||
|
@ -1286,15 +1302,10 @@ And of course, we note that
|
||||||
(\x y -> x+y) ⇔ (+)
|
(\x y -> x+y) ⇔ (+)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
Finally
|
Finally
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v8.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v8.hs
|
||||||
-- Version 8
|
-- Version 8
|
||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
||||||
evenSum :: Integral a => [a] -> a
|
evenSum :: Integral a => [a] -> a
|
||||||
|
@ -1327,7 +1338,8 @@ The =(.)= function corresponds to mathematical composition.
|
||||||
|
|
||||||
We can take advantage of this operator to η-reduce our function:
|
We can take advantage of this operator to η-reduce our function:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v9.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v9.hs
|
||||||
-- Version 9
|
-- Version 9
|
||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
||||||
evenSum :: Integral a => [a] -> a
|
evenSum :: Integral a => [a] -> a
|
||||||
|
@ -1336,7 +1348,8 @@ We can take advantage of this operator to η-reduce our function:
|
||||||
|
|
||||||
Also, we could rename some parts to make it clearer:
|
Also, we could rename some parts to make it clearer:
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(evenSum_v10.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle evenSum_v10.hs
|
||||||
-- Version 10
|
-- Version 10
|
||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
||||||
sum' :: (Num a) => [a] -> a
|
sum' :: (Num a) => [a] -> a
|
||||||
|
@ -1365,7 +1378,7 @@ Updating version 10 is extremely easy:
|
||||||
squareEvenSum' = evenSum . (map (^2))
|
squareEvenSum' = evenSum . (map (^2))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
We just had to add another "transformation function"[^0216].
|
We just had to add another "transformation function".
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
map (^2) [1,2,3,4] ⇔ [1,4,9,16]
|
map (^2) [1,2,3,4] ⇔ [1,4,9,16]
|
||||||
|
@ -1375,9 +1388,9 @@ 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.
|
We didn't have to modify anything /inside/ the function definition.
|
||||||
This makes the code more modular.
|
This makes the code more modular.
|
||||||
But in addition you can think more mathematically about your function.
|
But in addition you can think more mathematically about your functions.
|
||||||
You can also use your function interchangably with others, as needed.
|
You can also use your functions interchangeably with others, as needed.
|
||||||
That is, you can compose, map, fold, filter using your new function.
|
That is, you can /compose/, map, fold, filter using your new function.
|
||||||
|
|
||||||
Modifying version 1 is left as an exercise to the reader ☺.
|
Modifying version 1 is left as an exercise to the reader ☺.
|
||||||
|
|
||||||
|
@ -1394,7 +1407,7 @@ Unfortunately, using pure functional programming isn't well suited to all
|
||||||
usages.
|
usages.
|
||||||
Or at least such a language hasn't been found yet.
|
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
|
One of the great powers of Haskell is the ability to create DSL (Domain
|
||||||
Specific Language) making it easy to change the programming paradigm.
|
Specific Language) making it easy to change the programming paradigm.
|
||||||
|
|
||||||
In fact, Haskell is also great when you want to write imperative style
|
In fact, Haskell is also great when you want to write imperative style
|
||||||
|
@ -1409,10 +1422,6 @@ to understand when and how to use it.
|
||||||
But before talking about this Haskell super-power, we must talk about
|
But before talking about this Haskell super-power, we must talk about
|
||||||
another essential aspect of Haskell: /Types/.
|
another essential aspect of Haskell: /Types/.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
main = print $ evenSum [1..10]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
** Types
|
** Types
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: types
|
:CUSTOM_ID: types
|
||||||
|
@ -1421,8 +1430,10 @@ another essential aspect of Haskell: /Types/.
|
||||||
#+CAPTION: Dali, the madonna of port Lligat
|
#+CAPTION: Dali, the madonna of port Lligat
|
||||||
[[./salvador-dali-the-madonna-of-port-lligat.jpg]]
|
[[./salvador-dali-the-madonna-of-port-lligat.jpg]]
|
||||||
|
|
||||||
|
#+MACRO: tldr @@html:<abbr title="too long; didn't read">tl;dr:</abbr> @@
|
||||||
|
|
||||||
#+BEGIN_QUOTE
|
#+BEGIN_QUOTE
|
||||||
%tldr
|
{{{tldr}}}
|
||||||
|
|
||||||
- =type Name = AnotherType= is just an alias and the compiler doesn't
|
- =type Name = AnotherType= is just an alias and the compiler doesn't
|
||||||
mark any difference between =Name= and =AnotherType=.
|
mark any difference between =Name= and =AnotherType=.
|
||||||
|
@ -1436,8 +1447,8 @@ In Haskell, types are strong and static.
|
||||||
Why is this important?
|
Why is this important?
|
||||||
It will help you /greatly/ to avoid mistakes.
|
It will help you /greatly/ to avoid mistakes.
|
||||||
In Haskell, most bugs are caught during the compilation of your program.
|
In Haskell, most bugs are caught during the compilation of your program.
|
||||||
And the main reason is because of the type inference during compilation.
|
And the main reason is because of the type checking during compilation.
|
||||||
Type inference makes it easy to detect where you used the wrong parameter
|
Type checking makes it easy to detect where you used the wrong parameter
|
||||||
at the wrong place, for example.
|
at the wrong place, for example.
|
||||||
|
|
||||||
*** Type inference
|
*** Type inference
|
||||||
|
@ -1461,15 +1472,13 @@ You can provide =square= with an =Int=, an =Integer=, a =Float= a
|
||||||
Proof by example:
|
Proof by example:
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
#+BEGIN_EXAMPLE
|
||||||
% ghci
|
~/t/hsenv> ghci
|
||||||
GHCi, version 7.0.4:
|
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
|
||||||
...
|
Prelude> let square x = x * x
|
||||||
Prelude> let square x = x*x
|
|
||||||
Prelude> square 2
|
Prelude> square 2
|
||||||
4
|
4
|
||||||
Prelude> square 2.1
|
Prelude> square 2.1
|
||||||
4.41
|
4.41
|
||||||
Prelude> -- load the Data.Complex module
|
|
||||||
Prelude> :m Data.Complex
|
Prelude> :m Data.Complex
|
||||||
Prelude Data.Complex> square (2 :+ 1)
|
Prelude Data.Complex> square (2 :+ 1)
|
||||||
3.0 :+ 4.0
|
3.0 :+ 4.0
|
||||||
|
@ -1481,15 +1490,12 @@ Now compare with the amount of code necessary in C:
|
||||||
|
|
||||||
#+BEGIN_SRC C
|
#+BEGIN_SRC C
|
||||||
int int_square(int x) { return x*x; }
|
int int_square(int x) { return x*x; }
|
||||||
|
|
||||||
float float_square(float x) {return x*x; }
|
float float_square(float x) {return x*x; }
|
||||||
|
|
||||||
complex complex_square (complex z) {
|
complex complex_square (complex z) {
|
||||||
complex tmp;
|
complex tmp;
|
||||||
tmp.real = z.real * z.real - z.img * z.img;
|
tmp.real = z.real * z.real - z.img * z.img;
|
||||||
tmp.img = 2 * z.img * z.real;
|
tmp.img = 2 * z.img * z.real;
|
||||||
}
|
}
|
||||||
|
|
||||||
complex x,y;
|
complex x,y;
|
||||||
y = complex_square(x);
|
y = complex_square(x);
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -1524,8 +1530,8 @@ int main() {
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
C++ does a far better job than C in this regard.
|
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
|
But for more complex functions the syntax can be hard to follow: see
|
||||||
article]] for example.
|
[[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 C++ you must declare that a function can work with different types.
|
||||||
In Haskell, the opposite is the case.
|
In Haskell, the opposite is the case.
|
||||||
|
@ -1549,7 +1555,8 @@ Generally, in Haskell:
|
||||||
You can construct your own types.
|
You can construct your own types.
|
||||||
First, you can use aliases or type synonyms.
|
First, you can use aliases or type synonyms.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
{{{lnk(type_constr_1.hs)}}}
|
||||||
|
#+BEGIN_SRC haskell :tangle type_constr_1.hs
|
||||||
type Name = String
|
type Name = String
|
||||||
type Color = String
|
type Color = String
|
||||||
|
|
||||||
|
@ -1591,7 +1598,7 @@ Another method is to create your own types using the keyword =data=.
|
||||||
|
|
||||||
Now if you switch parameters of =showInfos=, the compiler complains!
|
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
|
So this is a potential mistake you will never make again and the only price
|
||||||
is to be more verbose.
|
is to be a bit more verbose.
|
||||||
|
|
||||||
Also notice that constructors are functions:
|
Also notice that constructors are functions:
|
||||||
|
|
||||||
|
@ -1666,7 +1673,7 @@ 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
|
(=Eq=) and compare (=Ord=) your new data structure you can tell Haskell to
|
||||||
derive the appropriate functions for you.
|
derive the appropriate functions for you.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle list.hs
|
||||||
infixr 5 :::
|
infixr 5 :::
|
||||||
data List a = Nil | a ::: (List a)
|
data List a = Nil | a ::: (List a)
|
||||||
deriving (Show,Read,Eq,Ord)
|
deriving (Show,Read,Eq,Ord)
|
||||||
|
@ -1676,12 +1683,12 @@ When you add =deriving (Show)= to your data declaration, Haskell creates a
|
||||||
=show= function for you.
|
=show= function for you.
|
||||||
We'll see soon how you can use your own =show= function.
|
We'll see soon how you can use your own =show= function.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle list.hs
|
||||||
convertList [] = Nil
|
convertList [] = Nil
|
||||||
convertList (x:xs) = x ::: convertList xs
|
convertList (x:xs) = x ::: convertList xs
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle list.hs
|
||||||
main = do
|
main = do
|
||||||
print (0 ::: 1 ::: Nil)
|
print (0 ::: 1 ::: Nil)
|
||||||
print (convertList [0,1])
|
print (convertList [0,1])
|
||||||
|
@ -1704,9 +1711,7 @@ This prints:
|
||||||
|
|
||||||
We'll just give another standard example: binary trees.
|
We'll just give another standard example: binary trees.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle tree.hs
|
||||||
import Data.List
|
|
||||||
|
|
||||||
data BinTree a = Empty
|
data BinTree a = Empty
|
||||||
| Node a (BinTree a) (BinTree a)
|
| Node a (BinTree a) (BinTree a)
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
@ -1715,7 +1720,7 @@ We'll just give another standard example: binary trees.
|
||||||
We will also create a function which turns a list into an ordered binary
|
We will also create a function which turns a list into an ordered binary
|
||||||
tree.
|
tree.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle tree.hs
|
||||||
treeFromList :: (Ord a) => [a] -> BinTree a
|
treeFromList :: (Ord a) => [a] -> BinTree a
|
||||||
treeFromList [] = Empty
|
treeFromList [] = Empty
|
||||||
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
||||||
|
@ -1733,7 +1738,7 @@ Look at how elegant this function is. In plain English:
|
||||||
- the right subtree is the tree created from members of the list =xs=
|
- the right subtree is the tree created from members of the list =xs=
|
||||||
which are strictly superior to =x=.
|
which are strictly superior to =x=.
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
#+BEGIN_SRC haskell :tangle tree.hs
|
||||||
main = print $ treeFromList [7,2,4,8]
|
main = print $ treeFromList [7,2,4,8]
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
@ -1745,212 +1750,106 @@ Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)
|
||||||
|
|
||||||
This is an informative but quite unpleasant representation of our tree.
|
This is an informative but quite unpleasant representation of our tree.
|
||||||
|
|
||||||
Just for fun, let's code a better display for our trees.
|
I've added the =containers= package in the =shell.nix= file, it is time to
|
||||||
I simply had fun making a nice function to display trees in a general way.
|
use this library which contain an helper to show trees.
|
||||||
You can safely skip this part if you find it too difficult to follow.
|
|
||||||
|
|
||||||
We have a few changes to make.
|
#+BEGIN_SRC haskell :tangle pretty_tree.hs
|
||||||
We remove the =deriving (Show)= from the declaration of our =BinTree= type.
|
import Data.Tree (Tree,Forest(..))
|
||||||
And it might also be useful to make our BinTree an instance of (=Eq= and
|
import qualified Data.Tree as Tree
|
||||||
=Ord=) so we will be able to test equality and compare trees.
|
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
data BinTree a = Empty
|
||||||
data BinTree a = Empty
|
|
||||||
| Node a (BinTree a) (BinTree a)
|
| Node a (BinTree a) (BinTree a)
|
||||||
deriving (Eq,Ord)
|
deriving (Eq,Ord,Show)
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
Without the =deriving (Show)=, Haskell doesn't create a =show= method for
|
treeFromList :: (Ord a) => [a] -> BinTree a
|
||||||
us.
|
treeFromList [] = Empty
|
||||||
We will create our own version of =show=.
|
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
||||||
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
|
|
||||||
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
|
|
||||||
-- 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
|
|
||||||
treeFromList :: (Ord a) => [a] -> BinTree a
|
|
||||||
treeFromList [] = Empty
|
|
||||||
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
|
||||||
(treeFromList (filter (>x) xs))
|
(treeFromList (filter (>x) xs))
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
And now, we can play:
|
-- | Function to transform our internal BinTree type to the
|
||||||
|
-- type of Tree declared in Data.Tree (from containers package)
|
||||||
|
-- so that the function Tree.drawForest can use
|
||||||
|
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||||
|
binTreeToForestString Empty = []
|
||||||
|
binTreeToForestString (Node x left right) =
|
||||||
|
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
-- | Function that given a BinTree print a representation of it in the console
|
||||||
main = do
|
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||||
|
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||||
|
|
||||||
|
main = do
|
||||||
putStrLn "Int binary tree:"
|
putStrLn "Int binary tree:"
|
||||||
print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
|
prettyPrintTree $ treeFromList [7,2,4,8,1,3,6,21,12,23]
|
||||||
|
putStrLn "\nNote we could also use another type\n"
|
||||||
|
putStrLn "String binary tree:"
|
||||||
|
prettyPrintTree $
|
||||||
|
treeFromList ["foo","bar","baz","gor","yog"]
|
||||||
|
putStrLn "\nAs we can test equality and order trees, we can make tree of trees!\n"
|
||||||
|
putStrLn "\nBinary tree of Char binary trees:"
|
||||||
|
prettyPrintTree (treeFromList
|
||||||
|
(map treeFromList ["foo","bar","zara","baz","foo"]))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
#+begin_example
|
||||||
|
~/t/hsenv> runghc pretty_tree.hs
|
||||||
Int binary tree:
|
Int binary tree:
|
||||||
< 7
|
7
|
||||||
: |--2
|
|
|
||||||
: | |--1
|
+- 2
|
||||||
: | `--4
|
| |
|
||||||
: | |--3
|
| +- 1
|
||||||
: | `--6
|
| |
|
||||||
: `--8
|
| `- 4
|
||||||
: `--21
|
| |
|
||||||
: |--12
|
| +- 3
|
||||||
: `--23
|
| |
|
||||||
#+END_EXAMPLE
|
| `- 6
|
||||||
|
|
|
||||||
|
`- 8
|
||||||
|
|
|
||||||
|
`- 21
|
||||||
|
|
|
||||||
|
+- 12
|
||||||
|
|
|
||||||
|
`- 23
|
||||||
|
|
||||||
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
|
|
||||||
putStrLn "\nString binary tree:"
|
|
||||||
print $ treeFromList ["foo","bar","baz","gor","yog"]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
Note we could also use another type
|
||||||
|
|
||||||
String binary tree:
|
String binary tree:
|
||||||
< "foo"
|
"foo"
|
||||||
: |--"bar"
|
|
|
||||||
: | `--"baz"
|
+- "bar"
|
||||||
: `--"gor"
|
| |
|
||||||
: `--"yog"
|
| `- "baz"
|
||||||
#+END_EXAMPLE
|
|
|
||||||
|
`- "gor"
|
||||||
|
|
|
||||||
|
`- "yog"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
As we can test equality and order trees, we can make tree of trees!
|
As we can test equality and order trees, we can make tree of trees!
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
putStrLn "\nBinary tree of Char binary trees:"
|
|
||||||
print ( treeFromList
|
|
||||||
(map treeFromList ["baz","zara","bar"]))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
|
||||||
Binary tree of Char binary trees:
|
Binary tree of Char binary trees:
|
||||||
< < 'b'
|
Node 'f' Empty (Node 'o' Empty Empty)
|
||||||
: : |--'a'
|
|
|
||||||
: : `--'z'
|
+- Node 'b' (Node 'a' Empty Empty) (Node 'r' Empty Empty)
|
||||||
: |--< 'b'
|
| |
|
||||||
: | : |--'a'
|
| `- Node 'b' (Node 'a' Empty Empty) (Node 'z' Empty Empty)
|
||||||
: | : `--'r'
|
|
|
||||||
: `--< 'z'
|
`- Node 'z' (Node 'a' Empty (Node 'r' Empty Empty)) Empty
|
||||||
: : `--'a'
|
#+end_example
|
||||||
: : `--'r'
|
|
||||||
#+END_EXAMPLE
|
|
||||||
|
|
||||||
This is why I chose to prefix each line of tree display by =:= (except
|
Notice how duplicate elements aren't inserted in trees.
|
||||||
for the root).
|
For exemple the Char BinTree constructed from the list =foo= is just =f ->
|
||||||
|
o=.
|
||||||
#+CAPTION: Yo Dawg Tree
|
When =o= is inserted another time the second =o= is not duplicated.
|
||||||
[[./yo_dawg_tree.jpg]]
|
But more importantly it works also for our own =BinTree= notice how the
|
||||||
|
tree for =foo= is inserted only once.
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
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
|
|
||||||
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
|
We have this for (almost) free, because we have declared Tree to be an
|
||||||
instance of =Eq=.
|
instance of =Eq=.
|
||||||
|
|
||||||
|
@ -2023,46 +1922,6 @@ This code is mostly the same as the previous one.
|
||||||
deriving (Eq,Ord)
|
deriving (Eq,Ord)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
|
||||||
-- 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.
|
Suppose we don't mind having an ordered binary tree.
|
||||||
Here is an infinite binary tree:
|
Here is an infinite binary tree:
|
||||||
|
|
||||||
|
@ -2178,7 +2037,7 @@ Look at the result for
|
||||||
print $ treeTakeDepth 4 infTreeTwo
|
print $ treeTakeDepth 4 infTreeTwo
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
* Difficulty: Nightmare
|
* Difficulty: Hard
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: hell-difficulty-part
|
:CUSTOM_ID: hell-difficulty-part
|
||||||
:END:
|
:END:
|
||||||
|
@ -3219,7 +3078,7 @@ In particular, monads are very useful for:
|
||||||
If you have followed me until here, then you've done it! You know
|
If you have followed me until here, then you've done it! You know
|
||||||
monads[fn:7]!
|
monads[fn:7]!
|
||||||
|
|
||||||
* Difficulty: Hell
|
* Difficulty: Nightmarish
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: difficulty--hell
|
:CUSTOM_ID: difficulty--hell
|
||||||
:END:
|
:END:
|
||||||
|
@ -3249,6 +3108,13 @@ Haskell at least two or three times before it really clicked for them.
|
||||||
:CUSTOM_ID: web-application
|
:CUSTOM_ID: web-application
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
* Difficulty: Hell
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: difficulty--hell-be9a
|
||||||
|
:END:
|
||||||
|
|
||||||
|
This part will be for advanced Haskell code.
|
||||||
|
|
||||||
* Appendix
|
* Appendix
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: appendix
|
:CUSTOM_ID: appendix
|
||||||
|
|
10
src/posts/0010-Haskell-Now/list.hs
Normal file
10
src/posts/0010-Haskell-Now/list.hs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
infixr 5 :::
|
||||||
|
data List a = Nil | a ::: (List a)
|
||||||
|
deriving (Show,Read,Eq,Ord)
|
||||||
|
|
||||||
|
convertList [] = Nil
|
||||||
|
convertList (x:xs) = x ::: convertList xs
|
||||||
|
|
||||||
|
main = do
|
||||||
|
print (0 ::: 1 ::: Nil)
|
||||||
|
print (convertList [0,1])
|
35
src/posts/0010-Haskell-Now/pretty_tree.hs
Normal file
35
src/posts/0010-Haskell-Now/pretty_tree.hs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import Data.Tree (Tree,Forest(..))
|
||||||
|
import qualified Data.Tree as Tree
|
||||||
|
|
||||||
|
data BinTree a = Empty
|
||||||
|
| Node a (BinTree a) (BinTree a)
|
||||||
|
deriving (Eq,Ord,Show)
|
||||||
|
|
||||||
|
treeFromList :: (Ord a) => [a] -> BinTree a
|
||||||
|
treeFromList [] = Empty
|
||||||
|
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
||||||
|
(treeFromList (filter (>x) xs))
|
||||||
|
|
||||||
|
-- | Function to transform our internal BinTree type to the
|
||||||
|
-- type of Tree declared in Data.Tree (from containers package)
|
||||||
|
-- so that the function Tree.drawForest can use
|
||||||
|
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||||
|
binTreeToForestString Empty = []
|
||||||
|
binTreeToForestString (Node x left right) =
|
||||||
|
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||||
|
|
||||||
|
-- | Function that given a BinTree print a representation of it in the console
|
||||||
|
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||||
|
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||||
|
|
||||||
|
main = do
|
||||||
|
putStrLn "Int binary tree:"
|
||||||
|
prettyPrintTree $ treeFromList [7,2,4,8,1,3,6,21,12,23]
|
||||||
|
putStrLn "\nNote we could also use another type\n"
|
||||||
|
putStrLn "String binary tree:"
|
||||||
|
prettyPrintTree $
|
||||||
|
treeFromList ["foo","bar","baz","gor","yog"]
|
||||||
|
putStrLn "\nAs we can test equality and order trees, we can make tree of trees!\n"
|
||||||
|
putStrLn "\nBinary tree of Char binary trees:"
|
||||||
|
prettyPrintTree (treeFromList
|
||||||
|
(map treeFromList ["foo","bar","zara","baz","foo"]))
|
10
src/posts/0010-Haskell-Now/tree.hs
Normal file
10
src/posts/0010-Haskell-Now/tree.hs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
data BinTree a = Empty
|
||||||
|
| Node a (BinTree a) (BinTree a)
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
treeFromList :: (Ord a) => [a] -> BinTree a
|
||||||
|
treeFromList [] = Empty
|
||||||
|
treeFromList (x:xs) = Node x (treeFromList (filter (<x) xs))
|
||||||
|
(treeFromList (filter (>x) xs))
|
||||||
|
|
||||||
|
main = print $ treeFromList [7,2,4,8]
|
11
src/posts/0010-Haskell-Now/type_constr_1.hs
Normal file
11
src/posts/0010-Haskell-Now/type_constr_1.hs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
Loading…
Reference in a new issue