From 383b69bc759608e29a6794214367bdd7a2cbfaa1 Mon Sep 17 00:00:00 2001 From: "Yann Esposito (Yogsototh)" Date: Tue, 24 Dec 2019 01:23:05 +0100 Subject: [PATCH] Sentence split. --- src/posts/0010-Haskell-Now/index.org | 821 ++++++++++++++------------- 1 file changed, 421 insertions(+), 400 deletions(-) diff --git a/src/posts/0010-Haskell-Now/index.org b/src/posts/0010-Haskell-Now/index.org index 771324b..b39dadf 100644 --- a/src/posts/0010-Haskell-Now/index.org +++ b/src/posts/0010-Haskell-Now/index.org @@ -35,21 +35,18 @@ But a few things have changed in the Haskell world. This is beyond the scope of an introductory material in my opinion. But, while the learning curve is as steep as before the highest point of learning just jumped higher than before with each new GHC release. -3. Still no real consencus about how to work, learn, and use Haskell. - In my opinion there are three different perspective on Haskell that - could definitively change how you make decisions about different aspect - of Haskell programming. I belive the main groups of ideolgies are +3. Still no real consencus about how to work, learn, and use Haskell. In my + opinion there are three different perspective on Haskell that could + definitively change how you make decisions about different aspect of + Haskell programming. I believe the main groups of ideolgies are application developers, library developers and even language (mostly - GHC) developers. - I kind of find those tensions a proof of an healthy environment. - There are different solutions to the same problems and that is perfectly - fine. - This is quite different when you compare to other language ecosystems - where decisions are more controlled or enforced. - I feel fine with both approaches. - But you must understand that there is not really any central mindset - within Haskeller unlike I can find in some other programming language - communities. + GHC) developers. I kind of find those tensions a proof of an healthy + environment. There are different solutions to the same problems and that + is perfectly fine. This is quite different when you compare to other + language ecosystems where decisions are more controlled or enforced. I + feel fine with both approaches. But you must understand that there is + not really any central mindset within Haskeller unlike I can find in + some other programming language communities. 4. I think that Haskell is now perceived as a lot more serious programming language now. There are a lot more big projects written in Haskell not just toy @@ -164,14 +161,13 @@ The article contains five parts: - More on infinite tree; a more math oriented discussion about infinite trees -** Helpers :noexport: +** Helpers :noexport: :PROPERTIES: :CUSTOM_ID: helpers :END: #+MACRO: lnk @@html:$1 ⤓@@ - ** Install :PROPERTIES: :CUSTOM_ID: install @@ -255,10 +251,12 @@ Congratulations you should be ready to start now. [[./munch_TheScream.jpg]] 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". +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". {{{lnk(hello.hs)}}} #+BEGIN_SRC haskell :tangle hello.hs @@ -314,8 +312,8 @@ int main (int argc, char **argv) { 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 ()=. +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 @@ -334,25 +332,26 @@ 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. +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. +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. +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: @@ -362,15 +361,17 @@ Applying a function with the same parameters always returns the same value. /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. +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. +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. @@ -485,8 +486,8 @@ error_basic.hs:4:17: error: The problem: =4.2= isn't an Int. -The solution: don't declare a type for =f= for the moment and let -Haskell infer the most general type for us: +The solution: don't declare a type for =f= for the moment and let Haskell +infer the most general type for us: {{{lnk(float_basic.hs)}}} #+BEGIN_SRC haskell :tangle float_basic.hs @@ -500,12 +501,13 @@ Haskell infer the most general type for us: 22.93 #+end_example -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... +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: +But, what type should we declare? +To discover the type Haskell has found for us, just launch ghci: #+BEGIN_SRC % ghci @@ -525,8 +527,8 @@ Uh? What is this strange type? 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: +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 | |--------------------+---------------------------------------------------------------------------| @@ -550,8 +552,8 @@ 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... +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/. @@ -566,8 +568,8 @@ 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=). +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. @@ -576,8 +578,8 @@ 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: +More precisely =f 3 4= is equivalent to =(f 3) 4=. +Note =f 3= is a function: #+BEGIN_SRC haskell f :: Num a => a -> a -> a @@ -600,8 +602,8 @@ We could also have written: 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. +If you are not used to functional programming your brain should be starting +to heat up. It is time to make a real application. But just before that, we should verify the type system works as @@ -616,10 +618,9 @@ expected: #+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. - ------ +numbers like Float and for Integer. +As =2.4= is a Fractional number, =3= is then interpreted as being also a +Fractional number. If we force our function to work with different types, it will fail: @@ -635,12 +636,12 @@ If we force our function to work with different types, it will fail: main = print (f x y) #+END_SRC -The compiler complains. The two parameters must have the same type. +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]] +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]] * Essential Haskell :PROPERTIES: @@ -650,20 +651,21 @@ really watch this great (and funny) video: #+CAPTION: Kandinsky Gugg [[./kandinsky_gugg.jpg]] -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 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. +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. ** Notations :PROPERTIES: :CUSTOM_ID: notations :END: - **** Arithmetic :PROPERTIES: :CUSTOM_ID: arithmetic @@ -748,9 +750,10 @@ In Haskell strings are list of =Char=. #+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=. +/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 **** Tuples @@ -758,8 +761,8 @@ represent a stream of ASCII char, you should use =Data.ByteString=. :CUSTOM_ID: tuples :END: -The type of couple is =(a,b)=. Elements in a tuple can have different -types. +The type of couple is =(a,b)=. +Elements in a tuple can have different types. #+BEGIN_EXAMPLE -- All these tuples are valid @@ -796,8 +799,6 @@ f $ g $ h x ⇔ f (g (h x)) (f . g . h) x ⇔ f (g (h x)) #+END_EXAMPLE ------ - ** Useful notations for functions :PROPERTIES: :CUSTOM_ID: useful-notations-for-functions @@ -815,9 +816,10 @@ 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. +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/ @@ -826,8 +828,9 @@ considered a good practice to do so. 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. +Note =^= uses infix notation. +For each infix operator there its associated prefix notation. +You just have to put it inside parenthesis. #+BEGIN_SRC haskell square' x = (^) x 2 @@ -835,13 +838,15 @@ associated prefix notation. You just have to put it inside parenthesis. square'' x = (^2) x #+END_SRC -We can remove =x= in the left and right side! It's called η-reduction. +We can remove =x= in the left and right side! +It's called η-reduction. #+BEGIN_SRC haskell square''' = (^2) #+END_SRC -Note we can declare functions with ='= in their name. Here: +Note we can declare functions with ='= in their name. +Here: #+BEGIN_QUOTE =square= ⇔ =square'= ⇔ =square''= ⇔ =square'''= @@ -857,7 +862,8 @@ An implementation of the absolute function. #+END_SRC Note: the =if .. then .. else= Haskell notation is more like the =¤?¤:¤= -C operator. You cannot forget the =else=. +C operator. +You cannot forget the =else=. Another equivalent version: @@ -868,8 +874,8 @@ Another equivalent version: #+END_SRC #+BEGIN_QUOTE -Notation warning: indentation is /important/ in Haskell. Like in -Python, bad indentation can break your code! +Notation warning: indentation is /important/ in Haskell. +Like in Python, bad indentation can break your code! #+END_QUOTE #+BEGIN_SRC haskell @@ -899,16 +905,16 @@ The hard part can now begin. #+CAPTION: Biomechanical Landscape by H.R. Giger [[./hr_giger_biomechanicallandscape_500.jpg]] -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. +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. +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 @@ -928,18 +934,18 @@ start by providing an imperative solution (in JavaScript): } #+END_SRC -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. +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. +/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. +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) { @@ -963,9 +969,10 @@ simplicity I assume the int list ends with the first =0= value. } #+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: +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 even :: Integral a => a -> Bool @@ -1000,10 +1007,8 @@ use: Note that for any non empty list =l=, =l ⇔ (head l):(tail l)= ------ - -The first Haskell solution. The function =evenSum= returns the sum of -all even numbers in a list: +The first Haskell solution. +The function =evenSum= returns the sum of all even numbers in a list: #+BEGIN_SRC haskell -- Version 1 @@ -1056,8 +1061,9 @@ Here is an example of execution[fn:2]: 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. +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 evenSum :: Integral a => [a] -> a @@ -1067,8 +1073,6 @@ things can be improved here. First, we can generalize the type. main = do print $ evenSum [1..10] #+END_SRC ------ - Next, we can use sub functions using =where= or =let=. This way our =accumSum= function won't pollute the namespace of our module. @@ -1106,19 +1110,19 @@ Next, we can use pattern matching. else accumSum n xs #+END_SRC -What is pattern matching? Use values instead of general parameter -names[fn:3]. +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: +Instead of saying: =foo l = if l == [] then else = you simply state: #+BEGIN_SRC haskell 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 +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 foo l = let x = head l @@ -1143,8 +1147,6 @@ It makes our code both terser and easier to read. main = print $ evenSum [1..10] #+END_SRC ------ - In Haskell you can simplify function definitions by η-reducing them. For example, instead of writing: @@ -1177,8 +1179,6 @@ We use this method to remove the =l=: main = print $ evenSum [1..10] #+END_SRC ------ - *** Higher Order Functions :PROPERTIES: :CUSTOM_ID: higher-order-functions @@ -1187,9 +1187,9 @@ We use this method to remove the =l=: #+CAPTION: Escher [[./escher_polygon.png]] -To make things even better we should use higher order functions. What -are these beasts? Higher order functions are functions taking functions -as parameters. +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: @@ -1350,9 +1350,9 @@ 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. +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 @@ -1371,8 +1371,7 @@ We just had to add another "transformation function"[^0216]. 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. +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. @@ -1387,21 +1386,25 @@ 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]]. +[[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. +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. +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/. @@ -1430,20 +1433,21 @@ another essential aspect of Haskell: /Types/. 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. +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. +Type inference makes it easy to detect where you used the wrong parameter +at the wrong place, for example. *** Type inference :PROPERTIES: :CUSTOM_ID: type-inference :END: -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. +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: @@ -1451,9 +1455,10 @@ Here is a simple example, the =square= function in Haskell: 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: +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 @@ -1489,9 +1494,10 @@ Now compare with the amount of code necessary in C: 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: +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_SRC c++ #include @@ -1517,30 +1523,31 @@ int main() { } #+END_SRC -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. +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. +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: +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 ------ - *** Type construction :PROPERTIES: :CUSTOM_ID: type-construction :END: -You can construct your own types. First, you can use aliases or type -synonyms. +You can construct your own types. +First, you can use aliases or type synonyms. #+BEGIN_SRC haskell type Name = String @@ -1556,18 +1563,16 @@ synonyms. main = putStrLn $ showInfos name color #+END_SRC ------ - -But it doesn't protect you much. Try to swap the two parameter of -=showInfos= and run the program: +But it doesn't protect you much. +Try to swap the two parameter of =showInfos= and run the program: #+BEGIN_SRC haskell 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. +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=. @@ -1584,8 +1589,8 @@ Another method is to create your own types using the keyword =data=. 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 +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: @@ -1622,8 +1627,8 @@ Also you can use the record syntax: , fieldn :: [type of fieldn] } #+END_SRC -And many accessors are made for you. Furthermore you can use another -order when setting values. +And many accessors are made for you. +Furthermore you can use another order when setting values. Example: @@ -1635,15 +1640,13 @@ Example: img z ⇒ 4 #+END_SRC ------ - *** Recursive type :PROPERTIES: :CUSTOM_ID: recursive-type :END: -You already encountered a recursive type: lists. You can re-create -lists, but with a more verbose syntax: +You already encountered a recursive type: lists. +You can re-create lists, but with a more verbose syntax: #+BEGIN_SRC haskell data List a = Empty | Cons a (List a) @@ -1660,8 +1663,8 @@ constructors. 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. +(=Eq=) and compare (=Ord=) your new data structure you can tell Haskell to +derive the appropriate functions for you. #+BEGIN_SRC haskell infixr 5 ::: @@ -1669,9 +1672,9 @@ to derive the appropriate functions for you. 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. +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 convertList [] = Nil @@ -1691,8 +1694,6 @@ This prints: 0 ::: (1 ::: Nil) #+END_EXAMPLE ------ - *** Trees :PROPERTIES: :CUSTOM_ID: trees @@ -1744,16 +1745,14 @@ Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty) This is an informative but quite unpleasant representation of our tree. ------ +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. -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. +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 data BinTree a = Empty @@ -1761,19 +1760,21 @@ equality and compare trees. 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: +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 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. +Here is my version of how to show a binary tree. +Don't worry about the apparent complexity. +I made a lot of improvements in order to display even stranger objects. #+BEGIN_SRC haskell -- declare BinTree a to be an instance of Show @@ -1853,9 +1854,10 @@ Int binary tree: : `--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. +Now it is far better! +The root is shown by starting the line with the =<= character. +And each following line starts with a =:=. +But we could also use another type. #+BEGIN_SRC haskell putStrLn "\nString binary tree:" @@ -1948,13 +1950,13 @@ Binary tree of Binary trees of Char binary trees: #+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! +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! ** Infinite Structures :PROPERTIES: @@ -1966,8 +1968,8 @@ a tree containing a tree of trees! 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/]]. +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: @@ -2011,8 +2013,6 @@ Also, note in Haskell there is a notation for infinite lists and most functions will work with them. Also, there is a built-in function =take= which is equivalent to our =take'=. ------ - This code is mostly the same as the previous one. #+BEGIN_SRC haskell @@ -2063,15 +2063,16 @@ This code is mostly the same as the previous one. | otherwise = x:[] -- "x" #+END_SRC -Suppose we don't mind having an ordered binary tree. Here is an infinite -binary tree: +Suppose we don't mind having an ordered binary tree. +Here is an infinite binary tree: #+BEGIN_SRC haskell 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: +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 -- take all element of a BinTree @@ -2121,9 +2122,10 @@ interesting tree: 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: +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 -- apply a function to each node of Tree @@ -2134,9 +2136,9 @@ instead of list. Here is such a function: (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=. +/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: @@ -2181,19 +2183,21 @@ Look at the result for :CUSTOM_ID: hell-difficulty-part :END: -Congratulations for getting so far! Now, some of the really hardcore -stuff can start. +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: +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. +Be prepared, the answers might be complex. +But they are all very rewarding. ** Deal With IO :PROPERTIES: @@ -2204,11 +2208,11 @@ rewarding. [[./magritte_carte_blanche.jpg]] #+BEGIN_QUOTE - %tldr + tldr; A typical function doing =IO= looks a lot like an imperative program: - #+BEGIN_SRC haskell +#+BEGIN_SRC haskell f :: IO a f = do x <- action1 @@ -2231,16 +2235,18 @@ rewarding. 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. +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. +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 +Ask a user to enter a list of numbers. +Print the sum of the numbers. #+END_QUOTE #+BEGIN_SRC haskell @@ -2262,8 +2268,8 @@ getLine :: IO String print :: Show a => a -> IO () #+END_SRC -Or more interestingly, we note that each expression in the =do= block -has a type of =IO a=. +Or more interestingly, we note that each expression in the =do= block has a +type of =IO a=. #+BEGIN_SRC haskell main = do @@ -2281,8 +2287,8 @@ do 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: +Another important note about using =IO=: all lines in a do block must be of +one of the two forms: #+BEGIN_SRC haskell action1 :: IO a @@ -2297,14 +2303,14 @@ or -- value :: b #+END_SRC -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. +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. ------ - -Now let's see how this program behaves. For example, what happens if the -user enters something strange? Let's try: +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 @@ -2313,30 +2319,35 @@ 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. +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. +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 import Data.Maybe #+END_SRC -What is this thing? =Maybe= is a type which takes one parameter. Its -definition is: +What is this thing? +=Maybe= is a type which takes one parameter. +Its definition is: #+BEGIN_SRC haskell 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=. +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 maybeRead :: Read a => String -> Maybe a @@ -2345,8 +2356,8 @@ of this function. I use a lower level function than =read=: =reads=. _ -> 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=. +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 @@ -2369,15 +2380,18 @@ We simply have to test the value in our main function. 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 ()=). +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. +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: @@ -2387,10 +2401,8 @@ Why does purity matter? Among the many advantages, here are three: - 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. - ------ +This is why you should generally put as most code as possible inside pure +functions. Our next iteration will be to prompt the user again and again until she enters a valid answer. @@ -2422,17 +2434,19 @@ until the input is right. 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: +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. +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: @@ -2443,7 +2457,8 @@ Finally our main function is much simpler: print $ sum list #+END_SRC -We have finished with our introduction to =IO=. This was quite fast. +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 @@ -2536,31 +2551,34 @@ focus on the impure parts: 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. +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. +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 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: +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 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]. +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]: @@ -2587,11 +2605,12 @@ type: 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)=. +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: +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=. @@ -2599,20 +2618,20 @@ Haskell, in order to evaluate =f a b=, you have many choices: 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. +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: +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: +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 askUser :: World -> ([Integer],World) @@ -2649,16 +2668,17 @@ 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: +Fortunately, there is a better way to handle this problem. +We see a pattern. +Each line is of the form: #+BEGIN_SRC haskell 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: +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 f :: World -> (a,World) @@ -2674,8 +2694,9 @@ pattern: ... #+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. +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: @@ -2707,9 +2728,11 @@ With, of course: =actionN w :: (World) -> (a,World)=. #+CAPTION: Jocker pencil trick [[./jocker_pencil_trick.jpg]] -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: +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 bind :: (World -> (a,World)) @@ -2732,15 +2755,17 @@ Some examples of functions: #+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". +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. +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=: @@ -2750,8 +2775,8 @@ This new =IO a= type helps us simplify the type of =bind=: -> IO b #+END_SRC -It says that =bind= takes two IO actions as parameters and returns -another IO action. +It says that =bind= takes two IO actions as parameters and returns another +IO action. Now, remember the /important/ patterns. The first was: @@ -2779,8 +2804,8 @@ Doesn't it seem familiar? 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: +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 let (line1, w1) = getLine w0 in @@ -2794,9 +2819,10 @@ Now, using the =bind= function: (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. +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 let (line1,w1) = getLine w0 in @@ -2813,10 +2839,13 @@ Which is equivalent to: print (line1 ++ line2))))) w0 #+END_SRC -Didn't you notice something? Yes, no temporary World variables are used -anywhere! This is /MA/. /GIC/. +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=. +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 @@ -2825,8 +2854,8 @@ We can use a better notation. Let's use =(>>=)= instead of =bind=. (\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: +Merry Christmas Everyone! +Haskell has made syntactical sugar for us: #+BEGIN_SRC haskell do @@ -2848,8 +2877,8 @@ Is replaced by: 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=: +But what about the lines not using the =<-=? +Easy, another function =blindBind=: #+BEGIN_SRC haskell blindBind :: IO a -> IO b -> IO b @@ -2857,8 +2886,8 @@ But what about the lines not using the =<-=? Easy, another function 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. +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 @@ -2884,12 +2913,10 @@ Also, another function is quite useful. 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. - ------ +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. To finish, let's translate our example: @@ -2946,16 +2973,14 @@ Imagine what it would look like without the =(>>)= and =(>>=)=. #+begin_comment #+CAPTION: 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. +of the mouth of a fish itself out of a grenade. [[./dali_reve.jpg]] #+end_comment -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 +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 @@ -2966,10 +2991,11 @@ your code. - 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): +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 class Monad m where @@ -3012,14 +3038,16 @@ declared (basically): :CUSTOM_ID: maybe-is-a-monad :END: -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. +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. +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 deposit value account = account + value @@ -3054,9 +3082,7 @@ balance dipping below zero. print $ eligible 299 -- False #+END_SRC ------ - -Now, let's make it better using Maybe and the fact that it is a Monad +Now, let's make it better using Maybe and the fact that it is a Monad. #+BEGIN_SRC haskell deposit :: (Num a) => a -> a -> Maybe a @@ -3081,8 +3107,6 @@ Now, let's make it better using Maybe and the fact that it is a Monad print $ eligible 299 -- Nothing #+END_SRC ------ - Not bad, but we can make it even better: #+BEGIN_SRC haskell @@ -3109,9 +3133,9 @@ Not bad, but we can make it even better: #+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. +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: @@ -3134,8 +3158,8 @@ You could also replay these example with the definition of =(>>=)= for #+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. +We saw the utility of the =IO= monad. +But now for a cooler example, lists. *** The list monad :PROPERTIES: @@ -3145,8 +3169,8 @@ lists. #+CAPTION: Golconde de Magritte [[./golconde.jpg]] -The list monad helps us to simulate non-deterministic computations. Here -we go: +The list monad helps us to simulate non-deterministic computations. +Here we go: #+BEGIN_SRC haskell import Control.Monad (guard) @@ -3180,9 +3204,10 @@ For the list monad, there is also this syntactic sugar: 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: +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, @@ -3232,22 +3257,20 @@ Haskell at least two or three times before it really clicked for them. This section is not so much about learning Haskell. It is just here to discuss some details further. - ** More on Infinite Tree :PROPERTIES: :CUSTOM_ID: more-on-infinite-tree :END: -In the section Infinite Structures we saw some -simple constructions. Unfortunately we removed two properties from our -tree: +In the section 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. +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 tree section. @@ -3380,8 +3403,6 @@ Left as an exercise to the reader: - Prove there is no =shuffle= list so that, for any depth, the program ends. ------ - This code is mostly the same as the preceding one. #+BEGIN_SRC haskell @@ -3560,12 +3581,12 @@ Left as an exercise to the reader: :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. +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. +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.