From e3bbe54f986c3a26a438fa06706d41a6d1bce40a Mon Sep 17 00:00:00 2001 From: "Yann Esposito (Yogsototh)" Date: Tue, 30 Jul 2019 11:49:03 +0200 Subject: [PATCH] updated org --- .project.el.gpg | Bin 3286 -> 3398 bytes serve.sh | 2 +- src/about-me.org | 7 +- src/{assets => }/css/minimalist.css | 0 src/drafts/Haskell-the-Hard-Way.org | 3772 --------------------------- src/{assets => }/favicon.ico | Bin src/{assets => }/img/a.png | Bin src/index.org | 2 +- src/posts/project-el/index.org | 2 + 9 files changed, 9 insertions(+), 3776 deletions(-) rename src/{assets => }/css/minimalist.css (100%) delete mode 100644 src/drafts/Haskell-the-Hard-Way.org rename src/{assets => }/favicon.ico (100%) rename src/{assets => }/img/a.png (100%) diff --git a/.project.el.gpg b/.project.el.gpg index 7b391f350b27f1e306357d62fbc2afbfa2d597a3..15c8eb4a0689bd936c79d4825638a686ccbca07e 100644 GIT binary patch literal 3398 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 Uhw#A^UV7?Ce3OJXom>;DI$~>M8UO$Q diff --git a/serve.sh b/serve.sh index 4e63630..24465f0 100755 --- a/serve.sh +++ b/serve.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cd _site && sws --local --no-auth . --port 3001 +cd _site && sws -d --port 3001 . diff --git a/src/about-me.org b/src/about-me.org index ac7d983..e137a9f 100644 --- a/src/about-me.org +++ b/src/about-me.org @@ -5,11 +5,14 @@ #+DESCRIPTION: #+LANGUAGE: en #+LANG: en -#+OPTIONS: H:5 +#+OPTIONS: H:5 auto-id:t #+STARTUP: showeverything * Contact + :PROPERTIES: + :CUSTOM_ID: contact + :END: -- mail: @@html: Yann Esposito <yann@esposito.host>@@ +- email: @@html: Yann Esposito <yann@esposito.host>@@ - keybase: https://keybase.io/yogsototh - gitlab: https://gitlab.esy.fun diff --git a/src/assets/css/minimalist.css b/src/css/minimalist.css similarity index 100% rename from src/assets/css/minimalist.css rename to src/css/minimalist.css diff --git a/src/drafts/Haskell-the-Hard-Way.org b/src/drafts/Haskell-the-Hard-Way.org deleted file mode 100644 index 72333ec..0000000 --- a/src/drafts/Haskell-the-Hard-Way.org +++ /dev/null @@ -1,3772 +0,0 @@ -#+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 -
-#+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 - 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 -
-#+END_HTML - -01_basic/10_Introduction/20_very_basic.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Very basic Haskell - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Function declaration - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -A Type Example - -#+BEGIN_HTML -

-#+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 -
-#+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 -
-#+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 -

-  % ghci
-  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> let f x y = x*x + y*y
-  Prelude> :type f
-  f :: Num a => a -> a -> a
-  
-#+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 -
-#+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 -
-#+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 -

-#+END_HTML - -Essential Haskell - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Notations - -#+BEGIN_HTML -

-#+END_HTML - -#+BEGIN_HTML -
-#+END_HTML - -Arithmetic - -#+BEGIN_HTML -
-#+END_HTML - -#+BEGIN_EXAMPLE - 3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3) -#+END_EXAMPLE - -#+BEGIN_HTML -
-#+END_HTML - -Logic - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -Powers - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -Lists - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -Strings - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -Tuples - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -Deal with parentheses - -#+BEGIN_HTML -
-#+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 -
-#+END_HTML - -01_basic/20_Essential_Haskell/10a_Functions.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Useful notations for functions - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Hard Part - -#+BEGIN_HTML -

-#+END_HTML - -The hard part can now begin. - -#+BEGIN_HTML -

-#+END_HTML - -Functional style - -#+BEGIN_HTML -

-#+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 -
-#+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 -
-  % ghci
-  GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
-  Loading package ghc-prim ... linking ... done.
-  Loading package integer-gmp ... linking ... done.
-  Loading package base ... linking ... done.
-  Prelude> :load 11_Functions.lhs
-  [1 of 1] Compiling Main             ( 11_Functions.lhs, interpreted )
-  Ok, modules loaded: Main.
-  *Main> evenSum [1..5]
-  6
-  
-#+END_HTML - -Here is an example of execution[fn:2]: - -#+BEGIN_HTML -
-  *Main> evenSum [1..5]
-  accumSum 0 [1,2,3,4,5]
-  1 is odd
-  accumSum 0 [2,3,4,5]
-  2 is even
-  accumSum (0+2) [3,4,5]
-  3 is odd
-  accumSum (0+2) [4,5]
-  2 is even
-  accumSum (0+2+4) [5]
-  5 is odd
-  accumSum (0+2+4) []
-  l == []
-  0+2+4
-  0+6
-  6
-  
-#+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 -
-#+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 -
-#+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 else = You simply -state: - -#+BEGIN_SRC haskell :eval never-export - foo [] = - foo l = -#+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 -
-#+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 -
-#+END_HTML - -02_Hard_Part/15_Functions.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Higher Order Functions - -#+BEGIN_HTML -

-#+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 -
-      myfunc list = foo initialValue list
-      foo accumulated []     = accumulated
-      foo tmpValue    (x:xs) = foo (bar tmpValue x) xs
-  
-#+END_HTML - -Which can be replaced by: - -#+BEGIN_HTML -
-  myfunc list = foldl bar initialValue list
-  
-#+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 -
-#+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 -
-    evenSum [1,2,3,4]
-  ⇒ foldl' (+) 0 (filter even [1,2,3,4])
-  ⇒ foldl' (+) 0 [2,4]
-  ⇒ foldl' (+) (0+2) [4]
-  ⇒ foldl' (+) 2 [4]
-  ⇒ foldl' (+) (2+4) []
-  ⇒ foldl' (+) 6 []
-  ⇒ 6
-  
-#+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 -

-#+END_HTML - -Types - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Type inference - -#+BEGIN_HTML -

-#+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 - #include - using namespace std; - - template - T square(T x) - { - return x*x; - } - - int main() { - // int - int sqr_of_five = square(5); - cout << sqr_of_five << endl; - // double - cout << (double)square(5.3) << endl; - // complex - cout << square( complex(5,3) ) - << endl; - return 0; - } -#+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 -
-#+END_HTML - -02_Hard_Part/21_Types.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Type construction - -#+BEGIN_HTML -

-#+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 -
-#+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 -
-#+END_HTML - -02_Hard_Part/23_Types.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Recursive type - -#+BEGIN_HTML -

-#+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 -
-#+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)) -#+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 -
-#+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)) -#+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 -
-#+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 -
-#+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 -

-#+END_HTML - -Hell Difficulty Part - -#+BEGIN_HTML -

-#+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 -
-#+END_HTML - -03_Hell/01_IO/01_progressive_io_example.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Deal With IO - -#+BEGIN_HTML -

-#+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 -
-  main = do
-    putStrLn "Enter ... " :: IO ()
-    getLine               :: IO String
-    print Something       :: IO ()
-  
-#+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 -
-#+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 =. 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 -
-#+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 -

-#+END_HTML - -IO trick explained - -#+BEGIN_HTML -

-#+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 -
-#+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 -
-#+END_HTML - -03_Hell/02_Monads/10_Monads.lhs - -#+BEGIN_HTML -

-#+END_HTML - -Monads - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Maybe is a monad - -#+BEGIN_HTML -

-#+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 -
-#+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 -
-#+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 -
-#+END_HTML - -03_Hell/02_Monads/13_Monads.lhs - -#+BEGIN_HTML -

-#+END_HTML - -The list monad - -#+BEGIN_HTML -

-#+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 -

-#+END_HTML - -Appendix - -#+BEGIN_HTML -

-#+END_HTML - -This section is not so much about learning Haskell. It is just here to -discuss some details further. - -#+BEGIN_HTML -
-#+END_HTML - -04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs - -#+BEGIN_HTML -

-#+END_HTML - -More on Infinite Tree - -#+BEGIN_HTML -

-#+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)) -#+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 -
-#+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 ( [a] -> BinTree a - treeFromList [] = Empty - treeFromList (x:xs) = Node x left right - where - left = 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 -
-    safefilter' f l = if filter f (take 10000 l) == []
-                      then []
-                      else filter f l
-    
- #+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) - 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. diff --git a/src/assets/favicon.ico b/src/favicon.ico similarity index 100% rename from src/assets/favicon.ico rename to src/favicon.ico diff --git a/src/assets/img/a.png b/src/img/a.png similarity index 100% rename from src/assets/img/a.png rename to src/img/a.png diff --git a/src/index.org b/src/index.org index 211e154..8e196c8 100644 --- a/src/index.org +++ b/src/index.org @@ -12,4 +12,4 @@ Welcome to my personal blog. This is the 4th time I change the underlying technology behind my blog. - [[./archive.html][posts]] -- [[./about][about me]] +- [[./about-me.html][about me]] diff --git a/src/posts/project-el/index.org b/src/posts/project-el/index.org index 13e9b46..87e8400 100644 --- a/src/posts/project-el/index.org +++ b/src/posts/project-el/index.org @@ -72,6 +72,8 @@ already loaded. :CUSTOM_ID: solution :END: +Dowload the code [[file:auto-load-project.el][auto-load-project.el]] + #+begin_src elisp (defun y/init-project-el-auto-load () "Initialize the autoload of .project.el.gpg for projects"