Sincerely, I don’t understand ack don’t become a common command on all UNIX systems. I can no more live without. For me it is as essential as which or find.
Most of time it is enough. But it is far better with colored output. ack-grep in Ubuntu does that. As I couldn’t install it on my ‘Evil Company Server’, I had done one myself in very few lines:
…this movie must be watched knowing you’ll cannot resolve the solution. At his best you’ll can suggest an interpretation close to the one of David Lynch. I believe I had found a coherent interpretation which allow to follow the movie without being totally lost. I believed it can give the keys necessary to make its own idea of the movie…
+
+
Lost Higway is a really good movie. You keep watching it event it seem totally obscure. This is one of the strength of David Lynch.
+
The first time I watched Lost Highway, I was a bit lost. Here some of explanations of Lost Highway I found on the Internet:
+
+
Fred make a pact with the devil incarnated by the Mysterious Man,
+
Mysterious Man is a video camera,
+
Just the first part of the story is real. The rest is in the Fred’s imagination,
+
+
and I don’t speak about many point of view found in forums.
+
I finished to find two good site talking about this movie. But none of them still totally convinced me:
the second which state almost the same interpretation about the movie and explain with even more details is on jasonweb
+
+
Nonetheless, this movie must be watched knowing you’ll cannot resolve the solution. At his best you’ll can suggest an interpretation close to the one of David Lynch.
+
I believe I had found a coherent interpretation which allow to follow the movie without being totally lost. I believed it can give the keys necessary to make its own idea of the movie.
+
The Rorschach test
+
+
+
+
Like the protagonist, everybody see what he want to see in this movie. It is an invitation to think. Watch this movie is a little like watch a Rorschach’s test. What do we see in it? Everybody put its own personnality in the interpretation of the movie.
+
+
If you are mystic, you’ll see in the mysterious man a devil,
+
If you are more psychanalytics, you’ll see an inconscient part of the protagonist…
+
+
Generally, we stay in this movie and we fail explaining everything. There is almost always a point that don’t fit within the interpretation of the movie. This is why trying to find a unique good interpretation of this movie is a mistake.
+
Interprétation ≠ Explanation
+
I give an interpretation and not an explanation. Just to tell my vision of the movie should be very different from yours. There is certainly many coherent explanations.
+
I write this post because I believe I had found an interpretation which seems coherent for most of the movie.
+
Movie’s keys
+
+
All is in Fred’s memory
+
+
In a first, it is clear for me, it is not a fantastic movie. If you follow this line, you’ll face many problem explaining some scenes.
+
My hypothesis is the movie describe the Fred’s representation of reality. Each of his tries to escape reality will fail.
+
Fred had commited an horrible act, a murder, and try to repair his memory to accepts it. He’ll then create alternative realities.
+
+
In a first time he kills his wife (Renee) because he believes she cheated at him.
+
In the second part, he’s weaker and will be manipulated by the blond equivalent of Renee to kill Dick Laurent.
+
In a third part, he kills Dick Laurent
+
+
Why this interpretation can be valid?
+
Because of the dialog at the begining of the movie. Cops ask Fred if he’s own a video camera:
+
+
“Do you own a video camera?”
+“No, Fred hates them.”
+“I like to remember things my own way.”
+“What do you mean by that?”
+“How I remember them, not necessarily the way they happened.”
+
+
Then, what we see is not reality but the Fred’s perception. Fred is the God of the reality we see. This is why some God/Devil interpretation of the movie works not so bad.
+
Who is the mysterious man?
+
+
+
+
Who’s this mysterious man? He tells Fred it’s him who invited him in his house. He’s present at the party and in the house of Fred in the same time. Eyes wide open, looking everything Fred’s doing?
+
It’s a key of the movie. In my humble opinion, I believe it represents the bad part of Fred. Certainly jalousy. If I was catholic, I’ll said he’s Satan. He observe, film but don’t act. He helps Fred to kill Dick Laurent. Fred had let him enter and cannot let him go. As Iago of Shakespeare is imprisonned by its own jalousy. The Mysterious Man help Fred doing the acts of violence. It also force Fred to remember the reality.
+
When he makes love to his wife (Renee), he sees the face of the Mysterious Man instead of his wife’s face. In reality, it’s the same person for Fred. It should be her who’s the origin of his interior badness.
+
Who’s at the origin of the video tapes?
+
Certainly it’s the mysterious man (Fred himself) who makes them. Their reason should be:
+
+
Remember the reality to Fred. From Fred point-of-view, video tapes are the reality. He tries to forget reality. But, finally, the video tapes go to the end: the murder of his wife.
+
It may also be a reference to pornographic video tapes, made by Renee.
+
+
What really happened?
+
There is many possibilities here. But we have many indices. Here is a supposition.
+
#1 Hypothesis
+
The protagonist is a garagist fallen in love with a porno actress. He believe the producer is the bad guy who go again his will. Then he kills Dick Laurent.
+
#2 Hypothesis
+
He was really married, he had killed his wife. The the remorse let him create an alternate self, which live in a kind of perfect world. But after the time pass, his obsession about the murder came again. And nobody could know if he had killed Andy or not.
+
which one then?
+
The second hypothesis seems better. We can make much more interpretation with it. It explain in most part the strange phone call from Dick Laurent to Pete. But the first hypothesis remain coherent. And, we should probably make an in depth explanantion using the first hypothesis. And I’m not sure it would be better.
+
One of the strength of this movie is to understand there is many other coherent hypothesis. It is an expression of the Rashomon effect. Many different persons could describe in a coherent manner what they saw. But each description contradicts the others.
+
+
Conclusion
+
There is much to tell about this movie. But I believe I put all essential keys here. It is a proof this movie is not a random one.
+
I believe it is essential to remember the “test of Rorschach effet” when watching this movie.
+
I’d like to know or opinion ; is my interpration wrong?
My wife bought about 500€ (at least) of TV Shows on iTunes. She bought the first season of Battlestar Gallactica in english (she notified the language after the dowload). DRM make it impossible to play it with french sub-titles.
+
+
+WTF?
+
+
+
Result, my wife would never buy any TV show on iTunes. She don’t like DVD because it is not as easy to buy and to use than to simply download episodes.
+
+
Therefore far less money for you EVIL Copyrighter!!!!!
+
+
My wife won’t see these episodes. This is a ‘LOSE-LOSE’ cooperation.
I use git simply to synchronize stuff for personnal projects. Therefore, when I create a local branch I want most of time this branch to be created remotely.
I use Git to manage my personnal projects. I have a centralized repository which all my computer should synchronize with. Unfortunately I didn’t find clearly what I needed on the official Git documentation.
+
In two words, if you want to use an SVN workflow with Git (and all its advantages) here is how to proceed.
+
+
Initialisation
+
Suppose I’ve got a directory on my local computer containing a project I want to manage via Git. Here what to do:
Well, now, all seems ok, but you have to worry about two little things. Git is all about decentralisation and branches. It is very easy to manage one branch, or many branches on the same host. But synchronize branches on many hosts is not a natural operation.
+
This is why I created two simple scripts to automate this. One for creating a branch locally and remotely. And one to get remotely created branched on your local host.
+
Then when you want to create a new branch (locally and remotely) ; you simply have to do a:
+
+git-create-new-branch branch_name
+
+
and when you are on another computer and want to get locally all the remote branches you execute:
How to recompile your screensaver to be Snow Leopard(c) compatible
+
I upgraded to Mac OS X 10.6 Snow Leopard(c), and my YClock screensaver didn’t work on it. After searching on google, the problem seems to be just a recompilation away. Unfortunately, even recompiling it in 64 bit it didn’t work either. After a bit more research (thanks to ElectricSheep ).
+
I discovered the good parameters for compilation.
+
+
+
+
For now I didn’t compiled it to work also on Tiger and Leopard. I don’t know XCode enought to know how to make the Garbage collector to be disabled on 32 bits version and enabled on 64 bits version.
+
It was a bit difficult to discover these informations. Hope this post helped someone.
and a local socks proxy listening on port 9050 is launched. The socks proxy will transfer local requests via the ssh tunnel. Therefore I can surf locally as if I was on my own computer. I can put password and card number without fear the local wifi network to be sniffed. I simply need to configure my web browser to user the socks proxy on localhost and port 9050.
Here I don’t want to talk about how great socks proxy via ssh tunneling is but how to configure my local server.
+
I have Mac with Snow Leopard(c) at home and it is far from enough to modify the /etc/sshd.config file. The system use launchd to launch starting daemons.
Most of time I prefer not to use the same product as everybody and try some new. But this time I believe whosamung.us had too much ads on the page. I had to put their image on my website and they only give then number of user currently on the website, not the number of visits.
+
This is why I now use google analytics. The only problem, remains for pages with no javascript support.
iDisk should soon disapear. This entry is mainly obsolescent now.
+
Update (2009/10/28)
+
I updated my script which is now incremental. Since the writing of this article, Apple(c) had made many efforts about the bandwith of its European servers.
+
+
WebDav terror
+
I live in France and iDisk upload is just terrible. Upload speed remind me the old 56k modem. Most operations such as list the content of a directory take at least 30 seconds (for 15 elements). Renaming a directory fail most of time.
+
Apple(c) use a WebDav server to host files. It works on port 80 (like http). I realized WebDav via https work better (2 to 3 times faster with far less errors). But even https is too slow.
+
I upload from my Mac and sometimes from an Ubuntu PC (iDisk mounted with webdavfs).
+
Synchronize safely the website
+
Here is the script I use in order to synchronize my website with maximum safety. It try each operations until it works.
+
The idea are:
+
+
synchronize to a temporary folder then swap the name therefore the website isn’t accessible only during the swap time. It takes only the time of two rename.
+
reiterate all operations until they work (for example, renaming).
+
+
For now I use rsync which in fact is no more efficient than a simple cp with WebDav. And I should use a method to keep track of elements who have changed. before the publication.
+
In fact when I’m on a Mac, I use Transmit which is very cool and far more efficient than the Finder to synchronize files. After the synchronization, I swap the directories.
+
My script take a -s option in order to make only the swap option. It also take a -a in order to put the new index.html which should point to the new homepage (not the iWeb one).
+
In order to keep this script working for you, just modify the username by yours (the value of the mobileMeUser).
In fact this method works for old threads. But it fails to create new post threads. This is why I tried and be conquered by intensedebate, as you can see in the bottom of this page.
+
Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).
+
+
Before begining, I must state that I love Disqus.
+
I know there is a similar blog entry at Trephine.org. Here I just add a straight and easy way to load disqus asynchronously using jQuery.
+
I also know there is a jQuery plugin to make just that. Unfortunately I had some issue with CSS.
+
Now let’s begin.
+
+
Why?
+
Why should I want to load the disqus javascript asynchronously?
+
+
Efficiency: I don’t want my page to wait the complete execution of disqus script to load.
+
More independance: when disqus is down, my page is blocked!
+
+
+
How?
+
I give a solution with jQuery, but I’m certain it will work with many other js library.
If you forget the window.disqus_no_style=true; then your page will be blank. Simply because without this option, the javascript use a document.write action after the document was closed, which cause a complete erasing of it.
+
CSS
+
But with this option you still need to provide a CSS. This is why you have to copy the css code from the embed.js file and rewrite it in a CSS file. You can download the CSS I obtained.
+
+
Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.
I made a blog entry about how I tried to integrate Disqus. I had to wait Disqus comment to be displayed before loading correctly my page. This is why I tried to include it in a “non-blocking” way. Unfortunately, I had difficulties to make it works correctly.
+
Furthermore, it was not trivial to make comment to be shared between multiple version of the same page (each page has three differents representations, one for each language and one more for the multi-language version).
+
I am a bit sad to quit Disqus because I must confess giannii had helped me has efficiently as he could. But the problem I had with disqus are inherent to some design choice not simply technical ones.
+
During the time I tried to integrate Disqus I never tried Intense Debate. Now that I have tried, i must confess it does exactly what I needed.
+
In order to make it fully asynchronous, you’ve just to download their common js and replace the following line:
Here is how I done the tag cloud of my blog. It is done mostly in jQuery. All my site is static and pages are generated with nanoc. It is (in my humble opinion) the modern geek way to make a website. The tagcloud should work for machine with and without javascript.
+
This is why I’ll give only a Ruby Generator, not a full javascript generator. But you can easily translate from Ruby to Javascript.
Calculate the real size of each tag to be displayed.
+
I choosen not to use the full range of size for all the tag. Because if no tag has more than n (here 10) occurences, then it doesn’t deserve to be of the maximal size.
Here is my new script, it first create a map which associate to each file its hash. After that it compare this file to the remote one. Then for each different file, update the content.
+
Even with this script I also have some problem. Mostly due to ‘webdav’ issues. For example, renaming a folder work really badly (on Linux at least). I use webdavfs. For example:
+
+ mv folder folder2
+
+
It returns OK and I’ve got:
+
+ $ ls folder folder2
+
+
Booh….
+
In order to handle most webdav issues I use a framework in zsh. It handle almost all except the correct renaming of folder. Working on it… Anyway here is the code I use.
+
+
#!/usr/bin/env zsh
+
function samelineprint { print -n -P – "\r$*" }
+
avec 1 essai par seconde: 300 = 5 minutes
+
maxessais=300
+
try to create a directory until success
+
function trymkdir { target=“$1” print – mkdir -p $target local essai=1 while ! mkdir -p $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to copy until success
+
function trycp { element=“$1” target=“$2” if [[ ! -d ${target:h} ]]; then trymkdir ${target:h} fi local essai=1 print – cp $element $target while ! $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to remove until success
+
function tryrm { target=“$1” local essai=1 local options=’’ [[ -d $target ]] && options=‘-rf’ print – rm $options $target while ! rm $options $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done essai=1 while [[ -e $element ]]; do samelineprint “rm reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to rename until success
+
function tryrename { element=“$1” target=“$2” local essai=1 while [[ -e $target ]]; do samelineprint “Echec n°$essai le fichier $target existe déjà” ((essai++)) ((essai>maxessais)) && exit 5 sleep 1 done print – mv $element $target while ! mv $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 4 done essai=1 while [[ -e $element ]]; do samelineprint “mv reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to move until success
+function trymv { element=“$1” target=“$2” local essai=1 print – mv $element $target while ! mv $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done essai=1 while [[ -e $element ]]; do samelineprint “mv reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
+
And here is the code on how I synchronize my website. There is a little cryptic code. It correspond a problem caused by the bluecloth filter which is a markdown program made in ruby. Each time my email is written it is transformed differently. This is why I remove this part from the content of each html file. Without it, all my files containing email are different at each regeneration of my website.
+
+
#!/usr/bin/env zsh
+
Script synchronisant le site sur me.com
+
normalement, le site est indisponible le moins de temps possible
+
le temps de deux renommages de répertoire
+
get configuration
+
mostly directories
+
source $0:h/config
+
get trycp function (copy until success)
+
source $0:h/webdav-framework
+
if [[ $1 == ‘-h’ ]]; then print – “usage : $0:h [-h|-s|-d]” print – " -a sychronise aussi l’index" print – " -h affiche l’aide" print – " -d modification directe (pas de swap)" print – " -s swappe simplement les répertoires" fi
+
publication incrementale
+
function incrementalPublish { local ydestRep=destRepsuffix localRef=“$srcRep/map.yrf” print – “Creation du fichier de references” create-reference-file.sh > $localRef remoteRef="/tmp/remoteSiteMapRef.$$.yrf" if [[ ! -e "$ydestRep/map.yrf" ]]; then # pas de fichier de reference sur la cible print – “pas de fichier de reference sur la cible, passage en mode rsync” rsyncPublish swap else trycp “$ydestRep/map.yrf" "$remoteRef” typeset -U filesToUpdate filesToUpdate=( $(diff $localRef $remoteRef | awk ’/1/ {print $2}' ) ) if ((${#filesToUpdate} == 1)); then print – “Seul le fichier ${filesToUpdate} sera téléversé" elif ((${#filesToUpdate}<10)); then print –”${#filesToUpdate} fichiers seront téléversés :" print -- "${filesToUpdate}" else print – “${#filesToUpdate} fichiers seront téléversés” fi # copy all file with some differences # except the map in case of error for element in $filesToUpdate; do if [[ $element == “/map.yrf” ]]; then continue fi if [[ -e srcRepelement ]]; then trycp srcRepelement ydestRepelement else tryrm ydestRepelement fi done # if all went fine, copy the map file trycp $srcRep/map.yrf $ydestRep/map.yrf # remove the temporary file }
+
publication via rsync
+
function rsyncPublish { result=1 essai=1 while (( $result > 0 )); do print – rsync -arv $srcRep/ $destRep.tmp if ((!testmode)); then rsync -arv $srcRep/ destRep.tmpfiresult=? if (( $result > 0 )); then print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2 fi ((essai++)) done }
+if [[ “$1” = “-s” ]]; then swap else if [[ “$1” = “-d” ]]; then suffix="" else suffix=“.tmp” fi print -P – “%BSync%b[${Root:t} => destRep : tsuffix]” incrementalPublish fi
+
+
This is my way to replace rsync with filesystem not handling it. Hope it is usefull. I’ll be happy to hear a way to handle the webdav rename folder problem. This is really annoying.
+
+
diff --git a/src/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js b/src/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js
new file mode 100644
index 0000000..6b047db
--- /dev/null
+++ b/src/Scratch/en/blog/2009-10-30-How-to-handle-evil-IE/code/ie.js
@@ -0,0 +1,17 @@
+// Remove all CSS I don't want to use on IE
+$('link[rel=stylesheet]').each(function(i)
+{
+ if (this.getAttribute('href') == '/css/layout.css')
+ this.disabled = true;
+ if (this.getAttribute('href') == '/css/shadows.css')
+ this.disabled = true;
+ if (this.getAttribute('href') == '/css/gen.css')
+ this.disabled = true;
+}) ;
+
+// Append the CSS for IE only
+$('head').append('');
+
+// I also add a message on top of the page
+$('body').prepend('
I believe the goal researched by minimalism is Focus. But I don’t believe minimalism should be the goal. Focus should be the goal, and I believe minimalism isn’t necessary to reach it.
+
This is why my design is not minimalist, but I decided to remove most of the navigation stuff of all pages of my website. May be I’ll prefer to hide the menu only when you are on blog article. For now, I hide the menu everywhere on the website.
+
+
technical details
+
For those who want the technical details behind the show/hide menu, here is the simple jQuery code.
Here is a live example of what appear while loading my pages.
+
+
+
+Hello! I’ve finished loading!
+
+
+Click me to see me disapear again.
+
+
+
Loading…
+
+
+
+
I first tried to integrate queryLoader, but it didn’t fill my needs.
+
The plugin add a black div to hide all the content. But as the script had to be launched at the end of the source code my website show for a small time.
+
In order to hide this small artefact, here is how I do that.
+
Code
+
In a first time, I added at the top of the body the div hiding all the content.
I discussed earlier why I prefer to hide my navigation menu. I finally decided to hide it only after a short time. Just the time needed for a user to see it. But how make it disappear only when it is not used for some time?
Here is a tip, I don’t know why, but I almost always forgot how to do that.
+
When you want to launch a command and this command should not be killed after you close your terminal. Here is how to accomplish that from command line:
I explain why I had so much difficulties to use Git. There is an “untaught rule” that make hard to work without. Until I read the good document.
+
“Cheap branches” aren’t designed to be totally isolated branches but rather should follow a “Master Branch”. There is a Standard Workflow to follow. If you don’t follow it, you prepare yourself with some hard time with Git.
+
+
My way to decentralisation
+
From SVN to Bazaar
+
I was a huge user of subversion (svn). Until the day I saw this video of Linus Torvald. Where he explain Git and all advantages of Decentralized Concurrent Versioning System(DCVS)
+
I must say I was completely convinced. And the more you learn about DCVS the more you see good reason to use them.
+
I then needed a versioning system for my team. As they were not used to open source versioning system except those heavy, with a GUI and with and administrator†
+
After some web searches, I founded three main choices:
After trying each other I chosen Bazaar. It has the simplest User Interface*. My choice was done.
+
From Bazaar to Git
+
It was really natural to learn when coming from subversion. The pull command corresponding to update, push command to commit. Commands like commit and update are still there if you want to use an SVN workflow.
+
After some times, reading on many blogs, I realize Git is far more popular and by influent people.
+
I then decide to use Git in particular to version this current website. But after trying it, I found it difficult and couter intuitive (I’ll speak a work about it later).
+
After calling for some help, when I say Bazaar is much simpler to learn, some people answer me that Git:
+
+
— SO-MUCH-EASY my 12 year old daughter uses it to version its school documents. She has no difficulties at all, creating branches, blah, blah, blah…
+
+
If a 12 years old girl has no problem with Git and I (with my Computer Science Ph.D.) have difficulties to uses it like I want, it is frustrating and humiliating. But what makes Git natural for some people and confusing for me?
+
I finally understood why reading a document I didn’t read before. It was the untaught part of the conception. The part every developer found so natural it is not necessary to say a word about it. But it was not natural for me.
+
† - I speak about ClearCase(c). I know there exists command line tools. But it was not the way my team used it.
+
* - I never really given its chance to Mercurial. The terminology they chosen was too far from the svn one. And I was used to it.
+
+
When you see explanation about branches and DCVS we imagine each branch is totally uncorrelated to each other, except when merging. Everything is magic. This is the “Parallel World” explanation. This way of seeing is explained more in depth in the real good article about branches on betterexplained.
+
Git was designed to manage the Linux Kernel. Git was designed using the concept of Patch instead of Parallel Worlds.
+
From one site Parallel World and Patches from the other. There is many equivalent notions in the two point of vue, but also some differences.
+
+
Bazaar seems base on the Parallel World vision which implies Patches
+
While Git seem base on the Patch model which will implie the creation of Parallel Worlds.
+
+
I will not argument about which is the best. Just tell my vision of DCVS come from the Parallel World vision and Git was designed the other way‡.
+
From Theory to Real Life Usage
+
I believe I understood conceptual mechanism under Git. But I had some difficulties with real usage. The worst point, the one I didn’t get before long was because I didn’t get really well the notion of Cheap Branching.
+
What is a Cheap Branch? If like me you come from Bazaar, it is a totally new notion. It is in fact the ability to create a branches all of them using the same directory.
+
You just have to launch a Git command and the local directory reflect the state of the branch you selected.
+
In theory, Cheap Branches are exactly like Bazaar branches. The word used is Branch and not Cheap Branch. But there is a slight difference between them. A slight difference between a Cloned Branch and a Cheap Branch.
+
A “Standard branch” is what is theoretically a kind of new Parallel World. But Cheap branch was designed to be future Patch for the main branch of the directory/Cloned branch.
+
Of course, I know anybody can state you can totally use Cheap branches as Cloned branches. But they weren’t designed for that. On daily usage, it is a bit uneasy to use it like this.
+
Here how Git cheap branches should be used (for more details see Git for Designers):
+
+
get or creation of a main repositoy The Great Repository
+
creation of a Cheap branch containing differences which have to be patched somewhere in the future into The Great Repository
+
+
Here’s how you should not use Git:
+
+
Get or creation of a repository
+
Create a cheap branch which will never push it’s modification to the main repository.
+
+
This simple minor difference of point of view confused me a lot.
+
Real Life Usage
+
Now I have understood all that. I understand why Git has some many people claiming it is the best DCVS.
+
Cheap branching notion is essential in Git and is a really useful feature. Particularly for this website. But, there are not exactly, completely parallel line of development. Because they are designed to path the main branch. Of course, it is not an obligation, but there are slight messages which tell you this should be used like that.
+
If I want to separate in a better way some branches I just have to Clone them. And I return exactly in branches Bazaar provided me.
+
Examples
+
For now, I prefer (from far) Bazaar terminology. They are cleaner and more understandable.
+
+bzr revert
+
+
Is clearer than
+
+git reset –hard HEAD
+
+
We can tell the same thing about
+
+bzr revert -r -3
+
+
which seems preferable to
+
+git reset –hard HEAD~3
+
+
Until now, it is not big business. But now, things will go worse. If we want to revert time on all the tree we use the keyword reset.
+
+OK
+
+
Now, if I want to revert time on on file. We should naturally imagine the command will be:
+
+git reset –hard FILE
+
+
+OF COURSE NOT!
+
+
The solution is:
+
+git checkout FILE
+
+
What? checkout !? Well, ok. I accept. why not? With Bazaar it is:
+
+git revert FILE
+
+
What I personally found far more natural.
+
But the command to change the current cheap branch is really hard to be accepted (from the User Interface point of view). With Bazaar it is:
+
+cd ../branch
+
+
Well yes. With Bazaar you have to change your directory to change your branch. It needs more disk resources but it is really clear. Which is my current branch, is just a pwd away. For Git here is the command:
+
+git checkout branch
+
+
WTF? I believed checkout was the key to get a file in some state (not the entire tree).
+
Then checkout is the same keyword used to get back in time on a file (BUT NOT ON ALL THE TREE where you have to use reset --hard) and to change current branch!
+
It is totally unnatural. Even if it is theoretically totally justified like you can see in the really good article Git for Computer Scientist. From the user point of vue, it is difficult to do worse than that. It is like somebody made it on purpose to make it the hardest possible to learn and understand.
+
+
+
— Try to find the good keyword for this operation
+
— Wrong! Try again!
+
— False, it is not yet right!
+
+
+
That were the Git bad side. But It has many advantages. Once you’ve understood the cheap branching paradigm. All became clearer for me after. Even if there is also some difficulties with the edit of the .git/config files (not user friendly at all).
+
‡ I must precise that I worked a lot with multi-modal logic and particularly about “Temporal Logics” (linear or not). This is why I was more inclined to see things this way. “Ah ! Just to remember my firsts love with computer science !”
+
+
Conclusion
+
DCVS vs. CVS ?
+
Was it a good idea to change to a decentralised versionning system? Clearly yes. Decentralisation give far much great possibilities. Such as working on a fix on a totally isolated branches.
+
Is Git better than Bazaar?
+
Speaking about features I’ll tell Git is the best. But Git was too much in my way. Is was exactly what I didn’t want for my first DCVS.
+
I shouldn’t have had those difficulties about understanding cheap branching which must be a patch. In reality, Git make a difference between the Tree and the Branch. Which is obviously not the case for Bazaar. Conceptually, bazaar is simpler to understand.
+
Finally
+
In conclusion, I use Git more often than Bazaar and I must say, that I have some preferences for Git. However, Git lack hardly clear commands name like revert. For now I don’t made alias to correct that. But may be one day I should do that.
A detailed tutorial of Git for people knowing very few about versions systems. You’ll understand utility of such program and how we use modern version control system. I try to stay as pragmatic as possible.
+
+
+
Begin with conclusion
+
Here is the list of sufficient and necessary command to use Git. There is very few. It is normal not to understand immediately but it is to gives you an idea. Even if this article is long, 95% of Git usage is in these 7 commands:
This article is written for people knowing very few about versionning systems. It is also written for those who had didn’t followed progress since CVS or subversion (SVN). This is why, in a first time I’ll explain quickly which are the goal of such systems. Secondly, I’ll explain how to install and configure Git. Then, I give the command for each feature a DCVS must have.
If you just want to use Gitimmediately, just read dark part. You read this part later to understand correctly foundations of version systems and not doing strange things.
+
+
Git is a DCVS, which means a Decentralized Concurrent Versions System. Let’s analyze each part of this long term:
+
Versions System
+
Firstly, versions system manage files. When somebody work with files without a versions system, the following happens frequently:
+
When you modify a somehow critical file you don’t want to loose. You copy naturally this file with another name. For example:
In consequence of what, the new file, play the role of backup. If you break everything, you can always return in the last state by overwriting your modifications. Of course, this method is not very professional and is a bit limited. If you make many modifications, you’ll end with many files with strange names like:
If you want to make it works correctly, you’ll have to use naming convention. Files take many place even if you modify most of time only some lines.
+
Fortunately, versions system are here to help.
+
You only have to signal you want a new version of a file and the versions system will do the job for you. It will record the backup where it could be easily recovered. Generally, systems version do it better than you, making the backup only of the modified lines and not the total file.
+
Once upon a time versions were managed for each file separately. I think about CVS. Then it naturally appears projects are a coherent set of files. Recover each file separately was a tedious work. This is why versions number passed from files to the entire project.
+
It is therefore possible to say, “I want to get back three days earlier”.
+
+
What gives versions system? (I didn’t mention everything at all)
+
+
automatic backups: back in time,
+
gives the ability to see differences between each version,
+
put a tag on some version to be able to refer to them easily,
+
gives the ability to see an historic of all modifications. Generally the user must add a comment for each new version.
+
+
+
concurrent:
+
Version Systems are already useful to manage its own projects. They help to organize and resolve partially backup problems. I say partially because you have to backup your repository on a decent file system. But versions system are really interesting is on projects done by many people.
+
Let’s begin by an example, a two person project ; Alex and Beatrice. On a file containing a Lovecraft’s gods list:
In real life, at the moment Beatrice want to send her modifications, the versions system alert her a modification had occurred on the server. Then she uses a command which pull the modification from the server to her local computer. And this command update her file. After that, Beatrice send again the new file on the server.
+
+
In what Concurrent Versions System help?
+
+
get without any problem others modifications,
+
send without any problem its own modifications to others,
+
manage conflicts. I didn’t speak about it, but sometimes a conflict can occur (when two different people modify the same line on a file for example). SVC help to resolve such problem. More on that later,
+
help to know who done what and when.
+
+
+
decentralized
+
This word became popular only recently about CVS. And it mainly means two things:
+
First, until really recently (SVN), you’ll have to be connected to the distant server to get informations about a project. Like get the history. New decentralized systems work with a local REPOSITORY (directory containing backups and many informations linked to the versions system functionalities). Hence, one can view the history of a project without the need of being connected.
+
All instances of a project can live independently.
+
To be more precise, DCVS are base on the branch notion.
+
Practically, it has great importance. It means, everybody work separately, and the system help to glue all their work.
+
It is even more than just that. It help to code independently each feature and bug fixes. Under other system it was far more difficult.
+
Typical example:
+
+
I develop my project. I’m ameliorating something. An urgent bug is reported.
+
With a DCVS I can easily, get back to the version with the bug. Fix it. Send the fix. Get back to my feature work. And even, use the fix for the new version with my new feature.
+
In a not decentralized version system, doing such a thing is possible but not natural. Decentralization means it become natural to use a branch for each separable work.
+
+
+
Advantages given by DCVS:
+
+
Ability to work offline,
+
Ability to create many atomic patches,
+
Help the maintenance of many different versions of the same application.
+
+
+
To resume
+
Let’s resume what we can easily do with DCVS:
+
Versions Systems
+
+
back in time,
+
list differences between versions,
+
name some versions to refer to them easily
+
show history of modifications
+
+
Concurrent
+
+
get others modifications,
+
send its modifications to others,
+
know who done what and when,
+
conflicts management.
+
+
Decentralized
+
+
Easily manipulate branches
+
+
Now let’s see how to obtain all these things easily with Git.
[color]
+ branch = auto
+ diff = auto
+ status = auto
+[alias]
+ st = status
+ co = checkout
+ br = branch
+ lg = log --pretty=oneline --graph
+ logfull = log --pretty=fuller --graph --stat -p
+ unstage = reset HEAD
+ # there should be an article on what this command do
+ uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
+ undomerge = reset --hard ORIG_HEAD
+ conflict = !gitk --left-right HEAD...MERGE_HEAD
+ # under Mac OS X, you should use gitx instead
+ # conflict = !gitx --left-right HEAD...MERGE_HEAD
+[branch]
+ autosetupmerge = true
+
You can achieve the same result using for each entry the command: git config --global. Next, configure your name and your email. For example, if your name is John Doe and your email is john.doe@email.com. Launch the following commands:
If there is no git server but you’ve got an ssh access. Just replace the git://host by ssh://user@host. In order not to type your password each time, use:
Reply to question and do not enter a password. Then copy your keys to the distant server. This is not the safest way to do this. The safest being, using ssh-agent.
Let do a small remark. If you don’t want to version every file. Typically intermediate compilation file, swap files… Then you need to exclude them. Just before launching the git add . command. You need to create a .gitignore file in the root directory of your project. This file will contain all exclude pattern. For example:
Now, if you want to create a repository on a distant server, it must not be in bare mode. The repository will contain only versionning informations, but not the files of the project. To achieve that:
You now have a local directory on your computer. It is versionned and you can say it is, because there is a .git directory at the root (and the root only) of your project. This directory contain all necessary informations for Git to version your project.
+
Now you only need to know how to use it.
+
Here we go!
+
Here is one from many way to use Git. This method is sufficient to work on a project. Not there is many other workflows.
Verify details of this modification: git status and git diff,
+
Add some file to be versionned if necessary: git add [file],
+
Save you modifications git commit -a -m "message",
+
Send your modifications to others: git push (redo a git pull if push return an error).
+
+
+
With these few commands you can use Git. Even if it is sufficient, you need to know one more thing before really begin ; How to manage conflicts.
+
Conflicts management
+
Conflicts can arise when you change the same line of code on the same file from another branch you’re merging. It can seems a bit intimidating, but with Git this kind of thing is really simple to handle.
Now you’re ready to use Git. Git provide many other functionnalities. Now we’ll see some Git usages older CVS couldn’t handle.
+
Why Git is cool?
+
Because with Git you can work on many part of some project totally independently. This is the true efficiency of decentralisation.
+
Each branch use the same directory. Then you can easily change your branch. You can also change branch when some files are modified. You can then dispatch your work on many different branches and merge them on one master branch at will.
+
Using the git rebase you can decide which modifications should be forget or merged into only one modification.
+
What does it mean for real usage? You can focus on coding. For example, you can code, a fix for bug b01 and for bug b02 and code a feature f03. Once finished you can create a branch by bug and by feature. And finally you can merge these modifications on a main branch.
+
All was done to code and decide how to organize your versions after. In other VCS it is not as natural as in Git.
+
With Git you can depend of many different sources. Then, there is not necessarily a ‘master’ repository where everybody puts its modifications.
+
What changes the most with Git when you come from SVN, it’s the idea of a centralized project on one server. With Git many people could work on the same project but not necessarily on the same repository as main reference. One can easily fix a bug and send a patch to many different versions of a project.
+
Command List
+
Command for each functionality
+
In the first part, we saw the list of resolved problem by Git. To resume Git should do:
+
+
get others modifications,
+
send modifications to others,
+
get back in time,
+
list differences between each version,
+
name some versions in order to refer easily to them,
It is unbelievable you cannot filter your call with an iPhone! The only reason I see for that is a negotiation with phone operator to force users to get phone advertising. It is simple unacceptable.
+
I’m a λ iPhone’s user. The only way to filter your call and to manage blacklist is to jailbreak your iPhone. And I don’t want to do that. Then, if like me you find it unacceptable, just write a line to Apple: http://www.apple.com/feedback/iphone.html
Why even if I believe git has many bad point I believe it is the best DCVS around to work with. This is why I first tell why I prefer Bazaar over Git. Secondly I’ll talk about the only advantage of git against Bazaar which lead me to prefer it.
+
+
The DCVS discovery
+
Before beginning this article, you should know I come from subversion. I find subversion to be a really good CVS. But I was converted to the decentralized ones.
+
There is two way of perceive version control system. Either you think in term of branches (see the really good article on betterexplained) or think in term of patches. Another way to say that, is weather you concentrate on vertices or on transitions of the graph of possible states of your project.
+
This is the second approach who was behind git and this is the first behind Bazaar. git was created by Linus Torvald in order to close some gap in the version system used to develop the Linux kernel. And patches is a term which is more present than ‘state’ in the development community.
+
I first was convinced by Bazaar. Why? Argument in favor of Bazaar were: user friendly, terminology close to the subversion one. And I tried a bit the two, and it was clearly more natural for me to use Bazaar. But after seeing so many people using git I decided to give it a serious try.
+
And it was so fastidious! The git terminology was horrible! And it is nothing to say it.
+
Where Bazaar is better than git
+
The first example, checkout is used to make only one thing from the technical point of vue. But from the user perspective, you make many different things with this word. Example:
And, like me, you remark, it is exactly the same command to make two completely different things. What occur when you have a pipo branch and a pipo file? By default, it change the current branch. In order to leave the ambiguity you have to use the following syntax:
It works, but it is clearly not really user friendly. Furthermore, checkout had a complete different signification in older CSV like cvs et svn. checkout was used to get a distant project locally.
+
Bazaar terminology is far more natural, because there is no command to change the current branch as there is only one branch per directory. Changing a branch in Bazaar is changing the current directory. I also believe it is the biggest problem of Bazaar, I’ll tell you why. And to undo things in Bazaar:
Except that this command is horrible. It forget revisions! Then you must use it with prudence. And you cannot tell other people working on the project you discard some changes. If someone had pulled the bad version, you are doomed. This is why you can also use:
Just to keep a backup branch. Without it we can definitively loose the current version HEAD. But some error may rest when there were some addition and deletion of files. The unique way to be really clean without any risk is to use the following command:
And with this command this is the only good way to undo things in a project and tell other contributor you reverted something. You simply revert version in backward order.
+
The rule is simple: NEVER use the git reset command on a version somebody else could have fetched
+
It was said. Discover the best method took me some time. I’d made many different tries. The safer and best way of reverting back your tree is to use this method. If you want to make it automatic just had the following alias in your ~/.gitconfig. Of course this alias will work only on environment having zsh installed. Which is the cas for most UNIX (Ubuntu, Mac OS X…).
After talking about the negatives points of git, now it’s time to speak about the very positive feature that make git the best DCVS in my humble opinion.
+
Cheap branching
+
You always work into the same main directory. For example, you can work on two fix in the same time. Say fix1 require you to work on file1 and fix2 to work on file2. You can work in any order on file1 and file2 in the master branch. And then go to branch fix1, commit file1 into it. Then go to branch fix2 and commit file2 into it. And finally merge the two branches fix1 and fix2 into master.
And this is great not to worry about working in the good branch and coding in the same time. You just worry about your code and then about the versionning system.
+
And I use this possibilities a lot. Working with bazaar, I often made the error to begin a change in the bad branch. then I have to copy my modifications, then revert. In short it was tiedous.
+
This is why I prefer using git on an every day usage. If Bazaar implement the same way of cheap branching than git. I should switch again.
I just found a way to change the default shell on Mac OS X. This note is mostly for me, but somebody else should find it useful. Just launch the following command:
+ if str.match(regexp) and not str.match(other_regexp) do_something
+
+
and you have to make this behaviour with only one regular expression. But, there exists a major problem: the complementary of a regular language might not be regular. Then, for some expression it is absolutely impossible to negate a regular expression.
+
But sometimes with some simple regular expression it should be possible†. Say you want to match everything containing the some word say bull but don’t want to match bullshit. Here is a nice way to do that:
+
+
# match all string containing ‘bull’ (bullshit comprised) /bull/
+
match all string containing ‘bull’ except ‘bullshit’
Let look closer. In the first line the expression is: bull([^s]|$), why does the $ is needed? Because, without it the word bull would be no more matched. This expression means:
+
+
The string finish by bull
+or,
+contains bull followed by a letter different from s.
+
+
And this is it. I hope it could help you.
+Notice this method is not always the best. For example try to write a regular expression equivalent to the following conditional expression:
+
+ # Begin with ‘a’: ^a # End with ‘a’: c$ # Contain ‘b’: .b. # But isn’t ‘axbxc’ if str.match(/^a.b.c/)andnotstr.match(/axbxc/) do_something end
+
This solution uses the maximal length of the string not to be matched. There certainly exists many other methods. But the important lesson is it is not straightforward to exclude something of a regular expression.
+
+
† It can be proved that any regular set minus a finite set is also regular.
In my previous post I had given some trick to match all except something. On the same idea, the trick to match the smallest possible string. Say you want to match the string between ‘a’ and ‘b’, for example, you want to match:
+
+a.....a......b..b..a....a....b...
+
+
Here are two common errors and a solution:
+
+/a.*b/
+a.....a......b..b..a....a....b...
+
+
The first error is to use the evil.*. Because you will match from the first to the last.
+
+/a.*?b/
+a.....a......b..b..a....a....b...
+
+
The next natural way, is to change the greediness. But it is not enough as you will match from the first a to the first b. Then a simple constatation is that our matching string shouldn’t contain any a nor b. Which lead to the last elegant solution.
+
+/a[^ab]*b/
+a.....a......b..b..a....a....b...
+
+
Until now, that was, easy. Now, just pass at the case you need to match not between a and b, but between strings. For example:
And it works in only 9 lines for any beginning and ending string. This solution should look less I AM THE GREAT REGEXP M45T3R, URAN00B, but is more convenient in my humble opinion. Further more, using this last solution prove you master regexp, because you know it is difficult to manage such problems with only a regexp.
+
+
† I know I used an HTML syntax example, but in my real life usage, I needed to match between en: and ::. And sometimes the string could finish with e::.
Strangely enough, I didn’t find any built-in tool to split a file by keyword. I made one myself in awk. I put it here mostly for myself. But it could also helps someone else. The following code split a file for each line containing the word UTC.
Regular expression are really useful. Unfortunately, they are not always the best way of doing things. Particularly when transformations you want to make are easy.
+
I wanted to know how to get file extension from filename the fastest way possible. There is 3 natural way of doing this:
+
+
# regexp str.match(/[^.]*/); ext=&
+
split
+
ext=str.split(‘.’)[-1]
+
File module
+ext=File.extname(str)
+
+
At first sight I believed that the regexp should be faster than the split because it could be many . in a filename. But in reality, most of time there is only one dot and I realized the split will be faster. But not the fastest way. There is a function dedicated to this work in the File module.
+puts “Get extname” Benchmark.bm do |x| x.report(“regexp:”) { n.times do str=tab[rand(4)]; str.match(/[^.]*/); ext=&; end } x.report(" split:“) { n.times do str=tab[rand(4)]; ext=str.split(‘.’)[-1] ; end } x.report(” File:") { n.times do str=tab[rand(4)]; ext=File.extname(str); end } end
+
+
And here is the result
+
+Get extname
+ user system total real
+regexp: 2.550000 0.020000 2.570000 ( 2.693407)
+ split: 1.080000 0.050000 1.130000 ( 1.190408)
+ File: 0.640000 0.030000 0.670000 ( 0.717748)
+
+
Conclusion of this benchmark, dedicated function are better than your way of doing stuff (most of time).
+puts “remove extension” Benchmark.bm do |x| x.report(" File:“) { n.times do str=tab[rand(4)]; path=File.expand_path(str,File.basename(str,File.extname(str))); end } x.report(”chomp:") { n.times do str=tab[rand(4)]; ext=File.extname(str); path=str.chomp(ext); end } end
+
+
and here is the result:
+
+remove extension
+ user system total real
+ File: 0.970000 0.060000 1.030000 ( 1.081398)
+chomp: 0.820000 0.040000 0.860000 ( 0.947432)
+
+
Conclusion of the second benchmark. One simple function is better than three dedicated functions. No surprise, but it is good to know.
If you have many branches it can be useful to use the following script/long command line.
+
+
# first clone your project $ git clone git@github.com:yogsototh/project.git
+
copy all branches
+$ zsh $ cd project $ for br in $( git br -a ); do case $br in remotes/) print $br ; case ${br:t} in master|HEAD) continue ;; ) git branch –track ${br:t} $br ;; esac ;; esac done
+
Here is a solution to maintain divergent branches in git. Because it is easy to merge by mistake. I give a script that encapsulate git in order to forbid some merge and warn you some merge should be dangerous.
+
+
how to protect against your own dumb
+
I work on a project in which some of my git branches should remain divergent. And divergences should grow.
+
I also use some branch to contain what is common between projects.
+
Say I have some branches:
+
master: common to all branches dev: branch devoted to unstable development client: branch with features for all client but not general enough for master clientA: project adapted for client A clientB: project adapted for client B
+
Here how I want to work:
+
+
+
+
And more precisely the branch hierarchy:
+
+
+
+
An arrow from A to B means, you can merge A in B. If there is no arrow from A to B that means it is forbidden to merge A in B. Here is the corresponding rubycode:
Having a :master => [ :dev, :client ] means you can merge master branch into dev and client.
+
If by mistake I make a git checkout master && git merge clientA, I made a mistake. This is why I made a script which encapsulate the git behaviour to dodge this kind of mistake.
+
But this script do far more than that. It also merge from top to down. The action allmerges will do:
+
+ git co dev && git merge master git co client && git merge master git co clientA && git merge client git co clientB && git merge client
+
+
That means, I can update all branches. The algorithm will not make loop even if there is a cycle in the branch hierarchy.
+
Here it is:
+
+
#!/usr/bin/env ruby # encoding: utf-8
+
architecture
+
+
master <-> dev
+
master -> client
+
clien -> clientA | clientB
+
+
merge using two of these branches should be
+
restricted to these rules
+
merge to one of these branch and an unknown one should
+
raise a warning, and may the option to add this new branch
if ARGV.length == 0 puts %{usage: $0:t [git_command or local_command]
+
local commands: allmerges: merge from top to down} exit 0 end
+
require ‘set’ $known_branches=Set.new $architecture.each do |k,v| $known_branches.add(k) v.each { |b| $known_branches.add(b) } end
+
def rec_merge(branch) if $architecture[branch].nil? return end $architecture[branch].each do |b| if $flag.has_key?(b.to_s + branch.to_s) next end flagname=branch.to_s + b.to_s if $flag.has_key?(flagname) next end if system %{eng checkout #{b}} if get_current_branch != b puts “Can’t checkout to #{b}” exit 2 end if system %{eng merge #{branch}} $flag[flagname]=true rec_merge(b) else exit 1 end else exit 1 end end end
+
def do_all_merges puts ‘Will merge from father to sons’ current_branch=get_current_branch $flag={} rec_merge(:master) system %{git co #{current_branch}} end
+
def do_merge current_branch=get_current_branch src_branch=ARGV[1].intern puts %{do_merge: #{src_branch} => #{current_branch}} if $known_branches.include?(current_branch) if $known_branches.include?(src_branch) if $architecture.has_key?(src_branch) and $architecture[src_branch].include?(current_branch) system %{git merge #{src_branch}} else puts %{Forbidden merge: #{src_branch} => #{current_branch}} end else puts %{Warning! #{src_branch} not mentionned in rb configuration} sleep 2 system %{git merge #{src_branch}} puts %{Warning! #{src_branch} not mentionned in rb configuration} end end end
+case ARGV[0] when ‘allmerges’ then do_all_merges when ‘merge’ then do_merge else system %{git #{ARGV.join(’ ’)}} end
+
+
All you need to do to make it work is simply to copy eng in a directory contained in your PATH.
+
Of course try to use as few as possible cherry-pick and rebase. This script was intended to work with workflow using pull and merge.
The more you wait to do something, the more difficult it is to start doing it. {: cite=“http://www.madore.org/~david/weblog/2010-05.html#d.2010-05-12.1752” }
+
+
I had to write another post for this blog. I had added many article idea in my todolist. But, I made many other things, and I’ve always said (until now), I’ll do this later. What changed my mind is the haunt of this simple remark about how to be productive in programming. > Stop write TODO in your code and make it now!
+> You’ll be surprised by the results.
+
In short: > Just do it! ou Juste fait le comme auraient dit les nuls.
+
Finally I’ll certainly write blog post more often for a short period of time.
+
What did I do?
+
I finished some web services/application for gridpocket(c).
+
I also finished to update my blog engine to nanoc3. The difficult part was to handle nicely multiple languages. But I should detail why in a future post.
+
I also have a real life. I enjoyed some vacancies with my family.
+
I work with Luc on a simple ruby REST/JSON/API oriented framework. It works fairly well, with really few bug until now. We planify to make a simple todolist tutorial. May be in two to three blog posts. This framework is not public for now. It will certainly be after we’ll create some simple web service with it and made a nice website for it.
+
Then what I plan to do from now:
+
+
finish to make a public web service (I believe it can be popular)
+
finish to write the associated iPhone application for it
+
finish to publish our private framework to make web services
+
publish some articles about this blog (at least 3)
+
+
diff --git a/src/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb b/src/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb
new file mode 100644
index 0000000..bc18f6c
--- /dev/null
+++ b/src/Scratch/en/blog/2010-05-19-How-to-cut-HTML-and-repair-it/code/repair_xml.rb
@@ -0,0 +1,24 @@
+# repair cutted XML code by closing the tags
+# work even if the XML is cut into a tag.
+# example:
+# transform '
For my main page, you can see, a list of my latest blog entry. And you have the first part of each article. To accomplish that, I needed to include the begining of the entry and to cut it somewhere. But now, I had to repair this cutted HTML.
In fact, it is not as difficult as it should sound first. The secret is, you don’t need to keep the complete tree structure to repair it, but only the list of not closed parents.
+
Given with our example, when we are after the first paragraph. we only have to close the div for class corps and the XML is repaired. Of course, when you cut inside a tag, you sould go back, as if you where just before it. Delete this tag and all is ok.
+
Then, all you have to do, is not remember all the XML tree, but only the heap containing your parents. Suppose we treat the complete first example, the stack will pass through the following state, in order:
The algorihm, is then really simple: ~~~~~~ {.html} let res be the XML as a string ; read res and each time you encouter a tag: if it is an opening one: push it to the stack else if it is a closing one: pop the stack.
+
remove any malformed/cutted tag in the end of res for each tag in the stack, pop it, and write: res = res + closed tag
+
return res ~~~~~~
+
And res contain the repaired XML.
+
Finally, this is the code in ruby I use. The xml variable contain the cutted XML.
Conclusion: The pragmatism shouldn’t mean “never use theory”.
+
+
+
Abstract (longer than tl;dr: )
+
For my job, I needed to resolve a problem. It first seems not too hard. Then I started working directly on my program. I entered in the infernal: try & repair loop. Each step was like:
+
+
– Just this thing to repair and that should be done.
+– OK, now that should just work.
+– Yeah!!!
+– Oops! I forgotten that…
+repeat until death
+
+
After two days of this Sisyphus work, I finally just stopped to rethink the problem. I took a pen, a sheet of paper. I simplified the problem, reminded what I learned during my Ph.D. about trees. Finally, the problem was crushed in less than 20 minutes.
+
I believe the important lesson is to remember that the most efficient methodology to resolve this pragmatic problem was the theoretical one. And therefore, argues opposing science, theory to pragmatism and efficiency are fallacies.
+
+
First: my experience
+
Apparently 90% of programmer are unable to program a binary search without bug. The algorithm is well known and easy to understand. However it is difficult to program it without any flaw. I participated to this contest. And you can see the results here1. I had to face a problem of the same kind at my job. The problem was simple to the start. Simply transform an xml from one format to another.
+
The source xml was in the following general format:
At first sight I believed it will be easy. I was so certain it will be easy that I fixed to myself the following rules:
+
+
do not use xslt
+
avoid the use of an xml parser
+
resolve the problem using a simple perl script[^2]
+
+
You can try if you want. If you attack the problem directly opening an editor, I assure you, it will certainly be not so simple. I can tell that, because it’s what I’ve done. And I must say I lost almost a complete day at work trying to resolve this. There was also, many small problems around that make me lose more than two days for this problem.
+
Why after two days did I was unable to resolve this problem which seems so simple?
+
What was my behaviour (workflow)?
+
+
Think
+
Write the program
+
Try the program
+
Verify the result
+
Found a bug
+
Resolve the bug
+
Go to step 3.
+
+
This was a standard workflow for computer engineer. The flaw came from the first step. I thought about how to resolve the problem but with the eyes of a pragmatic engineer. I was saying:
+
+
That should be a simple perl search and replace program.
+Let’s begin to write code
+
+
This is the second sentence that was plainly wrong. I started in the wrong direction. And the workflow did not work from this entry point.
+
Think
+
After some times, I just stopped to work. Tell myself “it is enough, now, I must finish it!”. I took a sheet of paper, a pen and began to draw some trees.
+
I began by make by removing most of the verbosity. I first renamed <item name="Menu"> by simpler name M for example. I obtained something like:
+
+
and
+
+
Then I made myself the following reflexion:
+
Considering Tree Edit Distance, each unitary transformation of tree correspond to a simple search and replace on my xml source2. We consider three atomic transformations on trees:
+
+
substitution: renaming a node
+
insertion: adding a node
+
deletion: remove a node
+
+
One of the particularity of atomic transformations on trees, is ; if you remove a node, all children of this node, became children of its father.
+
An example:
+
+r - x - a
+ \ \
+ \ b
+ y - c
+
+
If you delete the x node, you obtain
+
+ a
+ /
+r - b
+ \
+ y - c
+
+
And look at what it implies when you write it in xml:
Therefore, if there exists a one state deterministic transducer which transform my trees ; I can transform the xml from one format to another with just a simple list of search and replace directives.
+
Solution
+
Transform this tree:
+
+R - C - tag1
+ \ \
+ \ tag2
+ E -- R - C - tag1
+ \ \ \
+ \ \ tag2
+ \ E ...
+ R - C - tag1
+ \ \
+ \ tag2
+ E ...
+
+
to this tree:
+
+ tag1
+ /
+M - V - M - V - tag2 tag1
+ \ /
+ M --- V - tag2
+ \ \
+ \ M
+ \ tag1
+ \ /
+ V - tag2
+ \
+ M
+
+
can be done using the following one state deterministic tree transducer:
+
+
C -> ε
+E -> M
+R -> V
+
+
Wich can be traduced by the following simple search and replace directives:
I translate most of my blog entries in French and English. Most people advice me to have one file per language. Generally it ends with:
+
+Bonjour,
+
+voici un exemple de texte en français.
+[image](url)
+
+
+Hello,
+
+here is an example of english text.
+[image](url)
+
+
This way of handling translations force you to write completely an article in one language, copy it, and translate it.
+
However, most of time, there are common parts like images, source code, etc… When I want to correct some mistake on these parts, I have to make twice the work. With sometimes adding another mistake in only one language.
+
This is why I preferred to handle it differently. I use tags on a single file. Finally my files looks like:
+
+ fr: Bonjour,
+ en: Hello,
+
+ en: here is an example of english text.
+ fr: voici un exemple de texte en français.
+[image](url)
+
+
As I edit my files with vim, it is really easy to add fr: or en: at some line’s beginning using the useful C-v. However nanoc was conceived to be used for one language only. Or to be used with the first method. I tried to adapt nanoc to my usage. But after a while, I found it easier to pre-filter the nanoc work by a simple script. My script transform my file into two new files. And all work like a charm.
I published a light version of my blog engine based on nanoc yesterday night. By light, I mean a lighter, more portable CSS (without round border). You can get it on github.com.
Now your website reside into the output directory.
+
+
Documentation
+
Useful things to know
+
Multi-language
+
All files in multi are processed and copied in the content directory. For each file in multi, each line starting by ‘fr:’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.
+
If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.
+
Edition & Rendering
+
additional keywords
+
You can separate multi content div using the: n``ewcorps directive (see examples).
+
You can create div using b``egindiv(classname), e``nddiv. (See some existing blog entries for example). Use the class intro for the abstract part.
+
You can create nice description table using <``desc> (See source code for example).
+
Typography
+
In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by  . This enable not to have a line starting by a single special character.
+
You can use small caps using <sc> tags.
+
+
(c``) is replaced by (c).
+
(r``) is replaced by (r).
+
<``- is replaced by <-.
+
-``> is replaced by ->.
+
+
source code
+
To write source code you should use the following format:
+
~~~~~~ {.html} ~~~~~~ {.ruby} The code ~~~~~~
+
The file attribute is not required.
+
blog
+
If you want to make really long blog post, you can separate them into many files. To accomplish that, you simply have to make your files like:
All files are intended to be generated into the output/Scratch directory. This was made like that to work nicely with iWeb organisation of websites.
+
menu
+
The order of post is done using the menupriority meta-data in the header of the files.
+
You can hide some file from the menu by setting: isHidden: true in the header.
+
Details
+
To know more about this blog engine, you should look at nanoc project.
+
Then look at the files inside your project:
+
README.md : readme for the project (used by github) :: latest.md : symbolic link to the last blog entry :: multi/ : Directory containing multi-language articles :: tasks/ : scripts for website live :: config.yaml : global configuration file :: Rules : generation rules :: content/ : content files processed by nanoc :: layouts/ : erb templates :: lib/ : ruby libraries used to process files :: output/ : website :: Rakefile : not mandatory for this blog ::
This is a way not to count your own visits to your blog. First you should look on how I handle analytics. All analytics are handled in one javascript file, this make things really convenient.
+
Then you need to know my method use the jquery-cookie.
+
I check if the key admin is not set in the cookie before adding the visit.
Now accessing these files with you browser you can hide or appear in your statistics. You just have to think to access these file from all you browser.
At the loading of the page, I create a div as wide as the window. This div is a bit transparent. Then I hide it. I also take care to its z-index value to be sure it is behind all elements.
+
Then when we click on a div of class code, I copy the content into this new wide div, and I show it. Really simple but really efficient. No need to use a jQuery plugin.
Tried to make YPassword in jQuery and with Cappuccino.
+
Cappuccino nice in desktop browser but 1.4MB, not compatible with iPhone.
+
jQuery not as nice as the Cappuccino version but 106KB. iPhone compatible.
+
I’ll give a try to Dashcode 3.
+
+
+
+
+
Before start, I must say I know Cappuccino and jQuery are no more comparable than Cocoa and the C++ standard library. One is oriented for user interface while the other is and helper for low level programming. Nonetheless I used these two to make the same web application. This is why I compare the experience I had with each of them for this specific task.
+
+
I made a web version of my dashboard widget YPassword. It is a simple widget to manage your online password with a strong security and with a totally portable way. It is not intended to replace a keychain. It is more a password generator.
+
The first was made from the code of my dashboard widget and with some jQuery. You can try it here. I then made a second version with the Cappuccino. You can try it here.
+
What this widget do?
+
+
If you don’t mind about what does my widget and just want to know how the two frameworkcompare, you should go directly to the next part.
+
+
I manage my password on many site with a simple method. I remember a strong master password. And my password is mainly hash(masterPassword+domainName)
+
In reality I need a bit more informations to create a password:
In fact depending of websites, some give some strange constraint to your password:
+
+
minimal length,
+
maximal length,
+
must not contain a special character,
+
must contain a special character,
+
etc…
+
+
And if you want to change your password the leak number is here for that. All informations such as user name, maximal length can be stored in a public file. The only real secret is the master password.
+
If you want to know even more details you can always look at some of my old blog entries:
First, I’d like to say Cappuccino applications look simply awesome. It is like having a Cocoa application in your web browser. And this is great.
+
I also must admit I enjoyed making my application with Cappuccino. It is like programming for an iPhone application. If you are a bit familiar with Cocoa, you feel at home. If you don’t know anything about Cocoa, I suggest you to look at it. This is a really great framework to make User Interface. I am not a specialist, but I have done some MFC, java Swing1 and WXWindows User Interfaces (some years ago). And I must say, Cocoa is far better than those.
+
Cappuccino is a great web application oriented development. But there was also some drawbacks
I made some time to understand how to handle the onChange on the text fields.
+
Documentation lacked a bit of organisation.
+
It doesn’t work on iPhone.
+
It weighted 11MB to deploy.
+
It weight 1.3MB to load.
+
+
I didn’t use bindings because I believe they are not ready by now.
+
jQuery
+
The jQuery version of YPassword is not as finished as the Cappuccino one. Because, there is no slider directly with jQuery. I’d have to use jQueryUI. And I believe, using it will make the application weight far more than the today 106KB.
+
To make this version I simply copied my widget source code and adapted it. It was straightforward. But jQuery is not an application oriented framework. It is more a “dark side javascript animation framework”2.
+
I don’t have too much to say about the jQuery version. But this was way more low level programming than Cappuccino.
+
My conclusion
+
If you want to make an iPhone compatible web application just don’t use Cappuccino yet. If you want to make simple application like mine, I also believe, Cappuccino is a bit too much.
+
If you want to make a complex web oriented application, Cappuccino is a great choice. But you may have some difficulties to begin programming with it.
+
Finally, to terminate my web version of my widget, I’ll give a try to Dashcode 3. It seems to be a good alternative to create web widgets. I don’t know if Dashcode 3 is portable on non webkit browser. But if it is, it could be the end of projects like Cappuccino and Sproutcore.
+
+
+
+
If you are interested you can take a look at SEDiL. I am proud of the tree drawing view made from scratch.↩
+
I don’t want to feel like a troll I use jQuery to make some dark side animation on this blog. But the javascript on my blog is not needed except for commenting.↩
Some Reddit users reported my website was really long to load and to scroll. They thinks it was because of the ‘1px shadow’ I apply on all the text. I was a bit surprised, because I make some test into a really slow virtual machine. And all have always worked fine. In fact, what slow down so much are by order of importance:
+
+
Radial gradient on Chrome (not in Safari on Mac)
+
Box shadows on Firefox and Chrome
+
+
Gradient
+
On Safari on Mac there is absolutely no rendering time problem. But when I use Chrome under Linux it is almost unusable.
+
Safari and Chrome use webkit, when you access my website with javascript enabled, an additionnal browser specific CSS is loaded. Until now I switched only between: IE, Mozilla and Webkit. Now I added one more special case for Chrome. Now I continue to use gradient for Safari but no more on Chrome.
+
I didn’t tried to verify the efficiency of all new CSS 3 features. But I advise you not to use -webkit-gradient on Chrome. At least when the host is a Linux.
+
Box Shadows
+
I also detected that -moz-box-shadow elements slow down the rendering on Firefox under Linux. But there was very few time rendering issue with Safari on Mac.
+
Text Shadows
+
Many tell me to use text-shadows sparingly. But I believe it was not the real reason of the slow down. This is why I’ll get them back.
+
Conclusion
+
Do not use -webkit-gradient on Chrome browser yet. Try to use -moz-box-shadow sparingly.
tl;dr: I pretend to create a world to give examples of different meanings behind the word undecidability:
+
+
Undecidability due to measure errors,
+
Big errors resulting from small initial measure error,
+
Fractal undecidability ;
+
Logic Undecidability.
+
+
+
+
The Undecidabilities
+
+
If a demiurge made our world, he certainly had a great sense of humor. After this read, you should be convinced. I’ll pretend to be him. I’ll create a simplified world. A world that obey to simple mathematical rules. And I’ll tell you about one of the curse on this world: the undecidability. The inability to know if we had find the truth. The inability to predict many things that should be natural. Here begin the story.
+
+
+
+
+
In the beginning there was only void. Then a blog post beginning to be written. I breath profoundly to feel the weight of the act I will accomplish. A last tense moment and… I create the Universe. An incredible Universe which will exists only the time of this read. I’m the demiurge of this universe and you are its observer.
+
I construct this world using only simples rules. I decide that real rules of this world will be the one we believe are true for our world. Note the difference. For their world, everything we believe today is true for them. Their world is then probably simpler than our. Particularly, we can describe this world with axioms and mathematic rules. It is not so sure for our Universe. But we’ll talk about that later.
+
Lets the work begin. I create an Earth. I populate it with intelligent people, the Ys. Of course they are curious. In particular they try to understand their world. They believe that if they know the rules of their world they will be able to predict the consequences of most of their acts. They are so naive. If only they knew. But I’m here to help them.
+
I am a God who likes jokes. The first joke I make to Ys is to make their sense imperfect. Furthermore it is not possible to make perfect precise measure in my world. I let Ys ameliorate their technology but there is a theoretical limit to the best precision they can reach.
+
I’d like to precise that these people believe their world is flat. Some believe it is possible to find the rules of their Universe. Now, let the game begins.
+
Lets start easily, errors can cause undecidability.
+
Undecidability due to measure errors
+
Here is what one of them think:
+
+
All triangle I observe seems to share the same property. Each time I sum up their angles I obtain π radiants (180°). It is certainly a rule of my Universe. But how to be certain all triangle in my Universe share this property?
+
+
+
+
+
Some began to formalize the problem. They end by writing a mathematical proof. Marvelous! The proof seems correct, but, a problem remains. The proof is based on rules and axioms. How to be certain these rules and axioms are right in their world? They will try to measure again and again the sum of the angles of triangles. The measure will never fail. But they’ll never be certain the rules and axioms are right. Because then only way to verify all axioms depends of observation. And as a facetious god, I forbid perfect measure in observation.
+
Of course, they prey, they call me to help. And as any respectful god, I don’t answer. Ah ah ah! I’ve always loved to make these kind of thing. Let’s act as if I don’t exists. What a good joke!
+
They feel sad. But they have some hope:
+
Hope
+
+
If we make small measure error, we will make small predictive error.
+
+
Growing errors Undecidability
+
+
+
+
Unfortunately, the three bodies problem will crush this hope. Using Newton’s Universal Law of gravitation with two bodies, we can predict with precision what will be their position and speed in the future. Until there all seems OK. But now, add another body. All errors will grow. Errors will grow at a point that any prediction will be unusable.
+
Even with this bad news there is the hope to control the error.
+
+
May we should know the maximal measure error we can handle to predict something. And we should at least determine what we can predict and what we cannot.
+
+
Once again, this should not terminate has they hope.
+
Fractal Undecidability
+
Consider the following question:
+
+
+
+
Consider some GPS coordinates on a point around the cost of the “Bretagne” in France. The coordinates are 3 feet precise. Is the point in the water or on Earth?
+
For some coordinates it is not possible to know. Even if we are authorize to move a bit to dodge the borders. Because there are some zone in which all point could be a “border” for any size of the zone.
+
We can even imagine some mathematical structure where all points are at the border1.
+
Logical Undecidability
+
+
+
+
Until there all problem were undecidable because of measure errors. May be in a controlled world without any error we should be able to predict anything.
+I’m sorry to say no. Even in a self-contained mathematical world it can be possible to create object with an unpredictable behaviour.
+
It is the halting problem.
+
Theorem: It is undecidable given a description of a program, whether the program finishes running or will run forever. The idea of the proof is simple enough to be part of this article. And this is with pleasure I give you one here.
+
+
Suppose a program able to decide if any program halt exists. More precisely:
+
Hypothesis: there exists a program P such that:
+
+
P(x,y) return “stop” in a finite amount of time if x(y)2 will stop running.
+
P(x,y) return “loop” in a finite amount of time if x(y) will never stop running.
+
+
Remark: Any program can be represented as a string. Therefore, a program can be used as the input of another program. It is authorized to write P(x,x).
+
Let Q be the following program using the return value of P.
+
+Q(x) :
+ if P(x,x)="stop" then I enter in an infinite loop
+ if P(x,x)="loop" then I stop
+
+
Now, what is the value of P(Q,Q)?
+
+
if P(Q,Q) returns “stop” that imply by construction of Q that P(Q,Q) returns “loop”.
+
if P(Q,Q) returns “loop” that means by construction of Q that P(Q,Q) return “stop”.
+
+
Therefore there is a contradiction the only way to handle is by the non existence of the program P.
+
+
I am the demiurge of this imaginary world. And I cannot know the future of this world. Therefore, creative power isn’t equivalent to omnipotence.
+
+
After all this, it becomes difficult to know what we can believe. But it would be another error to throw away all our knowledge. In a future next part, I’ll explain what we can hope and what attitude we should have once we’ve realized most of truth are unaccessible.
Before my holidays many visitors tell me my website was too long to scroll. This is why I completely changed my website design. Now all should scroll smoothly on all platforms. I was inspired by Readability and iBooks(c) (the iPhone(c) application).
I had to send a mail using only command line. I was surprised it isn’t straightforward at all. I didn’t had pine nor mutt or anything like that. Just mail and mailx.
I tried it. And it works almost each times. But for my file, it didn’t worked. I compressed it to .gz, .bz2 and .zip. Using .bz2 format it worked nicely, but not with other formats. Instead of having an attached file I saw this in my email.
+
+begin 664 fic.jpg
+M(R$O=7-R+V)I;B]E;G8@>G-H"GAL%]M8UPB/BD\=F%L
+M=64O/B@\+VET96T^*2-<)#$\=F%L=64^)&ME>7=O
+
+Not really readable.
+After some research I found the solution.
+Use MIME instead of `uuencode`.
+
+Finally I made it manually using `sendmail`.
+I didn" t dare to use `telnet`. The command to use is: ~~~~~~ {.zsh} sendmail -t -oi < mailcontent.txt ~~~~~~ Of course you need to create the `mailcontent.txt` file. It should contains:
+From: from@mail.com
+To: to@mail.com
+Subject: View the attached file
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="-"
+
+This is a MIME encoded message. Decode it with "Decoder"
+or any other MIME reading software. Decoder is available
+at .
+---
+Content-Type: image/jpeg; name="fic.jpg"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline; filename="fic.jpg"
+
+H4sICB6Ke0wAA2Rjcl93aXRob3V0X2tleXdvcmQuY3N2ANSdW5ubOJPH7/e7
+7Brw+dmrTk8yk7yTSTaZeWd2b/TIIGy6MRAE7ng+/VaJgwF3g522SsxN2+3T
+/4eOJamqmARP+yibvI8ykUYim+x5EE2euBfIyd3byZ+fvvzr7svbu8ndTx/f
+...
+
+
And to obtain the “encoded” file in base64 I used:
+
uuencode -m fic.jpg fic.jpg ~~~~~~
+
That is all. Sometimes technology is so easy to use. If I need it another time I should consider to make a shell script to automatize this.
You can remark at the bottom of each page I provide a last modification date. This label was first calculated using the mtime of the file on the file system. But many times I modify this date just to force some recompilation. Therefore the date wasn’t a date of real modification.
+
I use git to version my website. And fortunately I can know the last date of real change of a file. This is how I do this with nanoc:
Of course I know it is really slow and absolutely not optimized. But it works as expected. Now the date you see at the bottom is exactly the date I modified the content of the page.
+
Edit: Thanks to Eric Sunshine and Kris to provide me some hints at cleaning my code.
I changed the design of my blog. Now it should be far cleaner. I believe I use no CSS3 feature and far less javascript. Of course before my website was perfectly browsable without javascript. Unfortunately some CSS3 feature are not mature enough on some browser. For more details you can read my older blog entry. But the major problem came from, font-shadow and gradients. Then my new design obey to the following rules:
+
+
no CSS element begining by ‘-moz’ or ‘-webkit’, etc…,
I’ve (re)discovered how to become S/MIME compliant. I am now suprised how easy it was. Some years ago it was far more difficult. Now I’m able to sign and encrypt my emails.
+
Why is it important?
+
Signing: it tell the other with an aboslute certitude the writer of the mail is you or at least used your computer.
+
Encrypt: because sometimes you need to be 100% sure a conversation remains private.
tl;dr: Played to process a wav file. C was easier and cleaner than Ruby.
+
edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didn’t had any portability consideration. This is only a hack.
+
+
I had to compute the sum of the absolute values of data of a .wav file. For efficiency (and fun) reasons, I had chosen C language.
+
I didn’t programmed in C for a long time. From my memory it was a pain to read and write to files. But in the end I was really impressed by the code I get. It was really clean. This is even more impressive knowing I used mostly low level functions.
+
A wav file has an header containing many metadata. This header was optimized to take as few space as possible. The header is then a block of packed bytes.
+
+
The 4th first bytes must contains RIFF in ASCII,
+
the following 4th Bytes is an 32 bits integer giving the size of the file minus 8, etc…
+
+
Surprisingly, I believe that reading this kind of file is easier in C than in most higher level language. Proof: I only have to search on the web the complete header format and write it in a struct.
Then, get an int value coded on two Bytes is also not a natural operation for high level language. In C, to read a sequence of 2 Bytes numbers I only had to write:
Of course it is only a hack. But we can see how easy and clean it should be to improve. As I say often: the right tool for your need instead of the same tool for all your needs. Because here C is clearly far superior than Ruby to handle this simple tasks.
+
I am curious to know if somebody know a nice way to do this with Ruby or Python.
+
edit: for compatibility reasons (64bit machines) used int16_t instead of short and int instead of int.
+
+
Edit (2): after most consideration about portability I made an hopefully more portable version. But I must confess this task was a bit tedious. The code remain as readable as before. But I had to use some compiler specific declaration to force the structure to be packed:
Therefore this implementation should for big and little endian architecture. However, it must be compiled with gcc. The new code make more tests but still don’t use mmap. Here it is:
tl;dr: I made a simple macro system for my blog. Now I juste have to write %latex and it show as LaTeX.
+
+
I added a macro system for my blog system. When we are used to LaTeX this lack can be hard to handle. Particularly when using mathematical notations. In the header of my files I simply write:
+
+
In the body it will replace every occurrence of:
+
+
%test by Just a test,
+
and %latex by LaTeX.
+
+
The source code is really simple. For nanoc user, simply put this file in your lib directory.
Update: I might change my mind now. Why? I just discovered a js2coffee converter. Furthermore Denis Knauf told me about a CoffeeScript.eval function. And as Denis said: “it is time to use Coffeescript as a javascript with Ruby-like syntax not a Ruby-like programming language”.
+
+
+
tl;dr: I would have loved to program client side using a Ruby-like syntax. But in the end, CoffeScript raised more disavantages than advantages.
+
+
Recently I read this entry on HackerNews. The most upvoted comment praised (within other) CoffeeScript. Recently I used a lot of javascript. After trying Sproutcore, Cappuccino, looking at backbone.js&javascriptMVC, I’ve finally decided to make my own minimal javascript MVC framework.1
+
I had to fight the horrible syntax of javascript. It was like experiencing a back-in-time travel:
+
+
Verbose Java-like syntax,
+
Strange and insanely Verbose Object Oriented Programming,
+
No easy way to refer to current instance of a class (this doesn’t work really well),
+
etc…
+
+
It was so annoying at a point, I had thinked about creating my own CoffeeScript.
+
I’d finished a first draft of my MVC javascript framework. Just after I learned about the existence of CoffeeScript, I immediately created a new git branch to try it.
+
Here is my experience:
+
+
I had to install node.js and use npm just to use CoffeeScript. It wasn’t a big deal but it wasn’t as straightfoward as I expected either.
+
Existing javascript file are not coffee compatible. I had to translate them by hand. There were no script to help me in this process. Thanks to vim, it wasn’t too hard to translate 90% of the javascript using some regexp. The --watch option of coffee was also really helpful to help in the translation. But I had to write my own shell script in order to follow an entire directory tree.
+
An unexpected event. I made some meta-programming in javascript using eval. But in order to work, the string in the eval must be written in pure javascript not in coffee. It was like writing in two different languages. Really not so good.
+
+
Conclusion
+
Advantages:
+
+
Readability: clearly it resolved most of javascript syntax problems
+
Verbosity: I gained 14% line, 22% words, 14% characters
+
+
Disadvantages:
+
+
Added another compilation step to see how my code behave on the website.
+
I had to launch some script to generate on change every of my javascript file
+
I have to learn another Ruby-like language,
+
meta-programming become a poor experience,
+
I must convince people working with me to:
+
+
install node.js, npm and CoffeeScript,
+
remember to launch a script at each code session,
+
learn and use another ruby-like language
+
+
+
The last two point were definitively really problematic for me.
+
But even if I’ll have to work alone, I certainly won’t use CoffeeScript either. CoffeeScript is a third party and any of their update can break my code. I experienced this kind of situation many times, and it is very annoying. Far more than coding with a bad syntax.
+
Digression
+
I am sad. I wanted so much to program on Web Client with a Ruby-like syntax. But in the end I think it is not for me. I have to use the horrible javascript syntax for now. At least I would have preferred a complete ruby2js script for example2. But I believe it would be a really hard task just to simulate the access of current class for example.
+
Typically @x translate into this.x. But the following code will not do what I should expect. Call the foo function of the current class.
Sometimes, the type determine a lot about the function★:
+
fst :: (a,b) -> a -- Only one choice
+snd :: (a,b) -> b -- Only one choice
+f :: a -> [a] -- Many choices
+-- Possibilities: f x=[], or [x], or [x,x] or [x,...,x]
+
+? :: [a] -> [a] -- Many choices
+-- can only rearrange: duplicate/remove/reorder elements
+-- for example: the type of addOne isn't [a] -> [a]
+addOne l = map (+1) l
+-- The (+1) force 'a' to be a Num.
The couple (F,fmap) is a \(\Hask\)'s functor if for any x :: F a:
+
fmap id x = x
+
fmap (f.g) x= (fmap f . fmap g) x
+
+
+
+
Haskell Functors Example: Maybe
+
+
data Maybe a = Just a | Nothing
+instance Functor Maybe where
+ fmap :: (a -> b) -> (Maybe a -> Maybe b)
+ fmap f (Just a) = Just (f a)
+ fmap f Nothing = Nothing
+
fmap (+1) (Just 1) == Just 2
+fmap (+1) Nothing == Nothing
+fmap head (Just [1,2,3]) == Just 1
+
+
+
Haskell Functors Example: List
+
+
instance Functor ([]) where
+ fmap :: (a -> b) -> [a] -> [b]
+ fmap = map
abusing notations denoting join by ⊙; this is equivalent to (F ⊙ F) ⊙ F = F ⊙ (F ⊙ F)
+
There exists η :: a -> F a s.t. η⊙F=F=F⊙η
+
+
+
+
Klesli composition
+
Now the composition works as expected. In Haskell ◎ is <=< in Control.Monad.
+
g <=< f = \x -> join ((fmap g) (f x))
+
f x = [x] ⇒ f 1 = [1] ⇒ (f <=< f) 1 = [1] ✓
+g x = [x+1] ⇒ g 1 = [2] ⇒ (g <=< g) 1 = [3] ✓
+h x = [x+1,x*3] ⇒ h 1 = [2,3] ⇒ (h <=< h) 1 = [3,6,4,9] ✓
+
+
+
+
We reinvented Monads!
+
A monad is a triplet (M,⊙,η) where
+
+
\(M\) an Endofunctor (to type a associate M a)
+
\(⊙:M×M→M\) a nat. trans. (i.e. ⊙::M (M a) → M a ; join)
+
\(η:I→M\) a nat. trans. (\(I\) identity functor ; η::a → M a)
+
+
Satisfying
+
+
\(M ⊙ (M ⊙ M) = (M ⊙ M) ⊙ M\)
+
\(η ⊙ M = M = M ⊙ η\)
+
+
+
+
Compare with Monoid
+
A Monoid is a triplet \((E,∙,e)\) s.t.
+
+
\(E\) a set
+
\(∙:E×E→E\)
+
\(e:1→E\)
+
+
Satisfying
+
+
\(x∙(y∙z) = (x∙y)∙z, ∀x,y,z∈E\)
+
\(e∙x = x = x∙e, ∀x∈E\)
+
+
+
+
Monads are just Monoids
+
+
A Monad is just a monoid in the category of endofunctors, what's the problem?
+
+
The real sentence was:
+
+
All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
+
+
+
+
Example: List
+
+
[] :: * -> * an Endofunctor
+
\(⊙:M×M→M\) a nat. trans. (join :: M (M a) -> M a)
+
\(η:I→M\) a nat. trans.
+
+
-- In Haskell ⊙ is "join" in "Control.Monad"
+join :: [[a]] -> [a]
+join = concat
+
+-- In Haskell the "return" function (unfortunate name)
+η :: a -> [a]
+η x = [x]
A LOT of monad tutorial on the net. Just one example; the State Monad
+
DrawScene to State Screen DrawScene ; still pure.
+
main = drawImage (width,height)
+
+drawImage :: Screen -> DrawScene
+drawImage screen = do
+ drawPoint p screen
+ drawCircle c screen
+ drawRectangle r screen
+
+drawPoint point screen = ...
+drawCircle circle screen = ...
+drawRectangle rectangle screen = ...
+
main = do
+ put (Screen 1024 768)
+ drawImage
+
+drawImage :: State Screen DrawScene
+drawImage = do
+ drawPoint p
+ drawCircle c
+ drawRectangle r
+
+drawPoint :: Point ->
+ State Screen DrawScene
+drawPoint p = do
+ Screen width height <- get
+ ...
+
+
+
fold
+
+
+
+
κατα-morphism
+
+
+
+
κατα-morphism: fold generalization
+
acc type of the "accumulator": fold :: (acc -> a -> acc) -> acc -> [a] -> acc
Algebra representing the (+1) and also knowing about the 0.
+
+
First example, make length on [Char]
+
+
+
κατα-morphism: Type work
+
+data StrF a = Cons Char a | Nil
+data Str' = StrF Str'
+
+-- generalize the construction of Str to other datatype
+-- Mu: type fixed point
+-- Mu :: (* -> *) -> *
+
+data Mu f = InF { outF :: f (Mu f) }
+data Str = Mu StrF
+
+-- Example
+foo=InF { outF = Cons 'f'
+ (InF { outF = Cons 'o'
+ (InF { outF = Cons 'o'
+ (InF { outF = Nil })})})}
+
+
+
+
κατα-morphism: missing information retrieved
+
type Algebra f a = f a -> a
+instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+
cata :: Functor f => Algebra f a -> Mu f -> a
+cata f = f . fmap (cata f) . outF
+
+
+
+
κατα-morphism: Finally length
+
All needed information for making length.
+
instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+length' :: Str -> Int
+length' = cata phi where
+ phi :: Algebra StrF Int -- StrF Int -> Int
+ phi (Cons a b) = 1 + b
+ phi Nil = 0
+
+main = do
+ l <- length' $ stringToStr "Toto"
+ ...
+
+
+
κατα-morphism: extension to Trees
+
Once you get the trick, it is easy to extent to most Functor.
+
type Tree = Mu TreeF
+data TreeF x = Node Int [x]
+
+instance Functor TreeF where
+ fmap f (Node e xs) = Node e (fmap f xs)
+
+depth = cata phi where
+ phi :: Algebra TreeF Int -- TreeF Int -> Int
+ phi (Node x sons) = 1 + foldr max 0 sons
+
+
+
Conclusion
+
Category Theory oriented Programming:
+
+
Focus on the type and operators
+
Extreme generalisation
+
Better modularity
+
Better control through properties of types
+
+
No cat were harmed in the making of this presentation.
Hakyll can be considered as a minimal cms. But more generally it is a library helping file generation. We can view it as an advanced build system (like make).
+
From the user perspective I blog this way:
+
+
I open an editor (vim in my case) and edit a markdown file. It looks like this
I open a browser and reload time to time to see the change.
+
Once I finished I’ve written a very minimal script which mainly do a git push. My blog is hosted on github.
+
+
Being short sighted one could reduce the role of Hakyll to:
+
+
create (resp. update) html file when I create (resp. change) a markdown file.
+
+
While it sounds easy, there are a lot of hidden details:
+
+
Add metadatas like keywords.
+
Create an archive page containing a list of all the posts.
+
Deal with static files.
+
Creating an rss feed.
+
Filter the content with some function.
+
Dealing with dependencies.
+
+
The work of Hakyll is to help you with these. But let’s start with the basic concepts.
+
The concepts and syntax
+
+
+
+
For each file you create, you have to provide:
+
+
a destination path
+
a list of content filters.
+
+
First, let’s start with the simplest case: static files (images, fonts, etc…). Generally, you have a source directory (here is the current directory) and a destination directory _site.
This program will copy static/foo.jpg to _site/static/foo.jpg. I concede this is a bit overkill for a simple cp. Now how to write a markdown file and generate an html one?
But horror! _site/posts/cthulhu.html is not a complete html file. It doesn’t have any header nor footer, etc… This is where you use templates. I simply add a new directive in the compile block.
As Sir Robin said just before dying before the Bridge of Death:
+
+
“That’s EASY!”
+
– Sir Robin, the Not-Quite-So-Brave-As-Sir-Lancelot
+
+
Real customization
+
Now that we understand the basic functionality. How to:
+
+
use SASS?
+
add keywords?
+
simplify url?
+
create an archive page?
+
create an rss feed?
+
filter the content?
+
add abbreviations support?
+
manage two languages?
+
+
Use SASS
+
That’s easy. Simply call the executable using unixFilter. Of course you’ll have to install SASS (gem install sass). And we also use compressCss to gain some space.
Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in 2013-03-20-My-New-Post.md.
+
I migrated from an older blog and didn’t want to change my url. Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata published. And the solution is here:
And base.html is a standard template (simpler than post.html).
+
archiveCtx provide a context containing an html representation of a list of posts in the metadata named posts. It will be used in the templates/archive.html file with $posts$.
postList returns an html representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is post-item.html:
generate partially rendered posts (no css, js, etc…)
+
+
We could then render the posts twice. One for html rendering and another time for rss. Remark we need to generate the rss version to create the html one.
+
One of the great feature of Hakyll is to be able to save snapshots. Here is how:
Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed don’t need a source markdown file. Then we create a new file from no one. Instead of using match, we use create:
Great idea certainly steal from nanoc (my previous blog engine)!
+
Filter the content
+
As I just said, nanoc was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a lot of filters in nanoc. I lost some because I don’t use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type String -> String.
+
Also we generally want prefilters (to filter the markdown) and postfilters (to filter the html after the pandoc compilation).
It will search for all string starting by ‘%’ and it will search in the Map if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.
Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.
The full code is here. And except from the main file, I use literate Haskell. This way the code should be easier to understand.
+
If you want to know why I switched from nanoc:
+
My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.
+
So I had to do something about it. I had two choices:
+
+
Correct my old code (in Ruby)
+
Duplicate the core functionalities with Hakyll (in Haskell)
+
+
I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.
+
So far I am very happy with the switch. A complete build is about 4x faster. I didn’t broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.
+
I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:
+
+
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs
new file mode 100644
index 0000000..1d9df7d
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/00_Introduction.lhs
@@ -0,0 +1,53 @@
+ ## Introduction
+
+In my
+[preceding article](/Scratch/en/blog/Haskell-the-Hard-Way/) I introduced Haskell.
+
+This article goes further.
+It will show how to use functional programming with interactive programs.
+But more than that, it will show how to organize your code in a functional way.
+This article is more about functional paradigm than functional language.
+The code organization can be used in most imperative language.
+
+As Haskell is designed for functional paradigm, it is easier to use in this context.
+In reality, the firsts sections will use an imperative paradigm.
+As you can use functional paradigm in imperative language,
+you can also use imperative paradigm in functional languages.
+
+This article is about creating an useful and clean program.
+It can interact with the user in real time.
+It uses OpenGL, a library with imperative programming foundations.
+Despite this fact,
+most of the final code will remain in the pure part (no `IO`).
+
+I believe the main audience for this article are:
+
+- Haskell programmer looking for an OpengGL tutorial.
+- People interested in program organization (programming language agnostic).
+- Fractal lovers and in particular 3D fractal.
+- People interested in user interaction in a functional paradigm.
+
+I had in mind for some time now to make a Mandelbrot set explorer.
+I had already written a [command line Mandelbrot set generator in Haskell](http://github.com/yogsototh/mandelbrot.git).
+This utility is highly parallel; it uses the `repa` package[^001].
+
+[^001]: Unfortunately, I couldn't make this program to work on my Mac. More precisely, I couldn't make the [DevIL](http://openil.sourceforge.net/) library work on Mac to output the image. Yes I have done a `brew install libdevil`. But even a minimal program who simply write some `jpg` didn't worked. I tried both with `Haskell` and `C`.
+
+This time, we will not parallelize the computation.
+Instead, we will display the Mandelbrot set extended in 3D using OpenGL and Haskell.
+You will be able to move it using your keyboard.
+This object is a Mandelbrot set in the plan (z=0),
+and something nice to see in 3D.
+
+Here are some screenshots of the result:
+
+blogfigure("GoldenMandelbulb.png","The entire Mandelbulb")
+blogfigure("3DMandelbulbDetail.png","A Mandelbulb detail")
+blogfigure("3DMandelbulbDetail2.png","Another detail of the Mandelbulb")
+
+And you can see the intermediate steps to reach this goal:
+
+blogimage("HGL_Plan.png","The parts of the article")
+
+From the 2nd section to the 4th it will be _dirtier_ and _dirtier_.
+We start cleaning the code at the 5th section.
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs
new file mode 100644
index 0000000..954c6f8
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/01_Introduction/hglmandel.lhs
@@ -0,0 +1,182 @@
+ ## First version
+
+We can consider two parts.
+The first being mostly some boilerplate[^011].
+And the second part more focused on OpenGL and content.
+
+[^011]: Generally in Haskell you need to declare a lot of import lines.
+ This is something I find annoying.
+ In particular, it should be possible to create a special file, Import.hs
+ which make all the necessary import for you, as you generally need them all.
+ I understand why this is cleaner to force the programmer not to do so,
+ but, each time I do a copy/paste, I feel something is wrong.
+ I believe this concern can be generalized to the lack of namespace in Haskell.
+
+ ### Let's play the song of our people
+
+> import Graphics.Rendering.OpenGL
+> import Graphics.UI.GLUT
+> import Data.IORef
+
+For efficiency reason[^010001], I will not use the default Haskell `Complex` data type.
+
+[^010001]: I tried `Complex Double`, `Complex Float`, this current data type with `Double` and the actual version `Float`. For rendering a 1024x1024 Mandelbrot set it takes `Complex Double` about 6.8s, for `Complex Float` about 5.1s, for the actual version with `Double` and `Float` it takes about `1.6` sec. See these sources for testing yourself: [https://gist.github.com/2945043](https://gist.github.com/2945043). If you really want to things to go faster, use `data Complex = C {-# UNPACK #-} !Float {-# UNPACK #-} !Float`. It takes only one second instead of 1.6s.
+
+> data Complex = C (Float,Float) deriving (Show,Eq)
+
+
+> instance Num Complex where
+> fromInteger n = C (fromIntegral n,0.0)
+> C (x,y) * C (z,t) = C (z*x - y*t, y*z + x*t)
+> C (x,y) + C (z,t) = C (x+z, y+t)
+> abs (C (x,y)) = C (sqrt (x*x + y*y),0.0)
+> signum (C (x,y)) = C (signum x , 0.0)
+
+We declare some useful functions for manipulating complex numbers:
+
+> complex :: Float -> Float -> Complex
+> complex x y = C (x,y)
+>
+> real :: Complex -> Float
+> real (C (x,y)) = x
+>
+> im :: Complex -> Float
+> im (C (x,y)) = y
+>
+> magnitude :: Complex -> Float
+> magnitude = real.abs
+
+
+ ### Let us start
+
+We start by giving the main architecture of our program:
+
+> main :: IO ()
+> main = do
+> -- GLUT need to be initialized
+> (progname,_) <- getArgsAndInitialize
+> -- We will use the double buffered mode (GL constraint)
+> initialDisplayMode $= [DoubleBuffered]
+> -- We create a window with some title
+> createWindow "Mandelbrot Set with Haskell and OpenGL"
+> -- Each time we will need to update the display
+> -- we will call the function 'display'
+> displayCallback $= display
+> -- We enter the main loop
+> mainLoop
+
+Mainly, we initialize our OpenGL application.
+We declared that the function `display` will be used to render the graphics:
+
+> display = do
+> clear [ColorBuffer] -- make the window black
+> loadIdentity -- reset any transformation
+> preservingMatrix drawMandelbrot
+> swapBuffers -- refresh screen
+
+Also here, there is only one interesting line;
+the draw will occur in the function `drawMandelbrot`.
+
+This function will provide a list of draw actions.
+Remember that OpenGL is imperative by design.
+Then, one of the consequence is you must write the actions in the right order.
+No easy parallel drawing here.
+Here is the function which will render something on the screen:
+
+> drawMandelbrot =
+> -- We will print Points (not triangles for example)
+> renderPrimitive Points $ do
+> mapM_ drawColoredPoint allPoints
+> where
+> drawColoredPoint (x,y,c) = do
+> color c -- set the current color to c
+> -- then draw the point at position (x,y,0)
+> -- remember we're in 3D
+> vertex $ Vertex3 x y 0
+
+The `mapM_` function is mainly the same as map but inside a monadic context.
+More precisely, this can be transformed as a list of actions where the order is important:
+
+~~~
+drawMandelbrot =
+ renderPrimitive Points $ do
+ color color1
+ vertex $ Vertex3 x1 y1 0
+ ...
+ color colorN
+ vertex $ Vertex3 xN yN 0
+~~~
+
+We also need some kind of global variables.
+In fact, global variable are a proof of a design problem.
+We will get rid of them later.
+
+> width = 320 :: GLfloat
+> height = 320 :: GLfloat
+
+And of course our list of colored points.
+In OpenGL the default coordinate are from -1 to 1.
+
+> allPoints :: [(GLfloat,GLfloat,Color3 GLfloat)]
+> allPoints = [ (x/width,y/height,colorFromValue $ mandel x y) |
+> x <- [-width..width],
+> y <- [-height..height]]
+>
+
+We need a function which transform an integer value to some color:
+
+> colorFromValue n =
+> let
+> t :: Int -> GLfloat
+> t i = 0.5 + 0.5*cos( fromIntegral i / 10 )
+> in
+> Color3 (t n) (t (n+5)) (t (n+10))
+
+And now the `mandel` function.
+Given two coordinates in pixels, it returns some integer value:
+
+> mandel x y =
+> let r = 2.0 * x / width
+> i = 2.0 * y / height
+> in
+> f (complex r i) 0 64
+
+It uses the main Mandelbrot function for each complex \\(c\\).
+The Mandelbrot set is the set of complex number \\(c\\) such that the following sequence does not escape to infinity.
+
+Let us define \\(f_c: \mathbb{C} \to \mathbb{C}\\)
+
+$$ f_c(z) = z^2 + c $$
+
+The sequence is:
+
+$$ 0 \rightarrow f_c(0) \rightarrow f_c(f_c(0)) \rightarrow \cdots \rightarrow f^n_c(0) \rightarrow \cdots $$
+
+Of course, instead of trying to test the real limit, we just make a test after a finite number of occurrences.
+
+> f :: Complex -> Complex -> Int -> Int
+> f c z 0 = 0
+> f c z n = if (magnitude z > 2 )
+> then n
+> else f c ((z*z)+c) (n-1)
+
+Well, if you download this file (look at the bottom of this section), compile it and run it this is the result:
+
+blogimage("hglmandel_v01.png","The mandelbrot set version 1")
+
+A first very interesting property of this program is that the computation for all the points is done only once.
+It is a bit long before the first image appears, but if you resize the window, it updates instantaneously.
+This property is a direct consequence of purity.
+If you look closely, you see that `allPoints` is a pure list.
+Therefore, calling `allPoints` will always render the same result and Haskell is clever enough to use this property.
+While Haskell doesn't garbage collect `allPoints` the result is reused for free.
+We did not specified this value should be saved for later use.
+It is saved for us.
+
+See what occurs if we make the window bigger:
+
+blogimage("hglmandel_v01_too_wide.png","The mandelbrot too wide, black lines and columns")
+
+We see some black lines because we have drawn less point than there is on the surface.
+We can repair this by drawing little squares instead of just points.
+But, instead we will do something a bit different and unusual.
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs
new file mode 100644
index 0000000..715db44
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/02_Edges/HGLMandelEdge.lhs
@@ -0,0 +1,152 @@
+ ## Only the edges
+
+
+
+> import Graphics.Rendering.OpenGL
+> import Graphics.UI.GLUT
+> import Data.IORef
+> -- Use UNPACK data because it is faster
+> -- The ! is for strict instead of lazy
+> data Complex = C {-# UNPACK #-} !Float
+> {-# UNPACK #-} !Float
+> deriving (Show,Eq)
+> instance Num Complex where
+> fromInteger n = C (fromIntegral n) 0.0
+> (C x y) * (C z t) = C (z*x - y*t) (y*z + x*t)
+> (C x y) + (C z t) = C (x+z) (y+t)
+> abs (C x y) = C (sqrt (x*x + y*y)) 0.0
+> signum (C x y) = C (signum x) 0.0
+> complex :: Float -> Float -> Complex
+> complex x y = C x y
+>
+> real :: Complex -> Float
+> real (C x y) = x
+>
+> im :: Complex -> Float
+> im (C x y) = y
+>
+> magnitude :: Complex -> Float
+> magnitude = real.abs
+> main :: IO ()
+> main = do
+> -- GLUT need to be initialized
+> (progname,_) <- getArgsAndInitialize
+> -- We will use the double buffered mode (GL constraint)
+> initialDisplayMode $= [DoubleBuffered]
+> -- We create a window with some title
+> createWindow "Mandelbrot Set with Haskell and OpenGL"
+> -- Each time we will need to update the display
+> -- we will call the function 'display'
+> displayCallback $= display
+> -- We enter the main loop
+> mainLoop
+> display = do
+> -- set the background color (dark solarized theme)
+> clearColor $= Color4 0 0.1686 0.2117 1
+> clear [ColorBuffer] -- make the window black
+> loadIdentity -- reset any transformation
+> preservingMatrix drawMandelbrot
+> swapBuffers -- refresh screen
+>
+> width = 320 :: GLfloat
+> height = 320 :: GLfloat
+
+
+
+
+This time, instead of drawing all points,
+we will simply draw the edges of the Mandelbrot set.
+The method I use is a rough approximation.
+I consider the Mandelbrot set to be almost convex.
+The result will be good enough for the purpose of this tutorial.
+
+We change slightly the `drawMandelbrot` function.
+We replace the `Points` by `LineLoop`
+
+> drawMandelbrot =
+> -- We will print Points (not triangles for example)
+> renderPrimitive LineLoop $ do
+> mapM_ drawColoredPoint allPoints
+> where
+> drawColoredPoint (x,y,c) = do
+> color c -- set the current color to c
+> -- then draw the point at position (x,y,0)
+> -- remember we're in 3D
+> vertex $ Vertex3 x y 0
+
+And now, we should change our list of points.
+Instead of drawing every point of the visible surface,
+we will choose only point on the surface.
+
+> allPoints = positivePoints ++
+> map (\(x,y,c) -> (x,-y,c)) (reverse positivePoints)
+
+We only need to compute the positive point.
+The Mandelbrot set is symmetric relatively to the abscisse axis.
+
+> positivePoints :: [(GLfloat,GLfloat,Color3 GLfloat)]
+> positivePoints = do
+> x <- [-width..width]
+> let y = maxZeroIndex (mandel x) 0 height (log2 height)
+> if y < 1 -- We don't draw point in the absciss
+> then []
+> else return (x/width,y/height,colorFromValue $ mandel x y)
+> where
+> log2 n = floor ((log n) / log 2)
+
+This function is interesting.
+For those not used to the list monad here is a natural language version of this function:
+
+
+positivePoints =
+ for all x in the range [-width..width]
+ let y be smallest number s.t. mandel x y > 0
+ if y is on 0 then don't return a point
+ else return the value corresonding to (x,y,color for (x+iy))
+
+
+In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically.
+To find the smallest number such that `mandel x y > 0` we use a simple dichotomy:
+
+> -- given f min max nbtest,
+> -- considering
+> -- - f is an increasing function
+> -- - f(min)=0
+> -- - f(max)≠0
+> -- then maxZeroIndex f min max nbtest returns x such that
+> -- f(x - ε)=0 and f(x + ε)≠0
+> -- where ε=(max-min)/2^(nbtest+1)
+> maxZeroIndex func minval maxval 0 = (minval+maxval)/2
+> maxZeroIndex func minval maxval n =
+> if (func medpoint) /= 0
+> then maxZeroIndex func minval medpoint (n-1)
+> else maxZeroIndex func medpoint maxval (n-1)
+> where medpoint = (minval+maxval)/2
+
+No rocket science here. See the result now:
+
+blogimage("HGLMandelEdges.png","The edges of the mandelbrot set")
+
+
+
+> colorFromValue n =
+> let
+> t :: Int -> GLfloat
+> t i = 0.5 + 0.5*cos( fromIntegral i / 10 )
+> in
+> Color3 (t n) (t (n+5)) (t (n+10))
+
+> mandel x y =
+> let r = 2.0 * x / width
+> i = 2.0 * y / height
+> in
+> f (complex r i) 0 64
+
+> f :: Complex -> Complex -> Int -> Int
+> f c z 0 = 0
+> f c z n = if (magnitude z > 2 )
+> then n
+> else f c ((z*z)+c) (n-1)
+
+
+
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs
new file mode 100644
index 0000000..fc20dc0
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/03_Mandelbulb/Mandelbulb.lhs
@@ -0,0 +1,378 @@
+ ## 3D Mandelbrot?
+
+Now we will we extend to a third dimension.
+But, there is no 3D equivalent to complex.
+In fact, the only extension known are quaternions (in 4D).
+As I know almost nothing about quaternions, I will use some extended complex,
+instead of using a 3D projection of quaternions.
+I am pretty sure this construction is not useful for numbers.
+But it will be enough for us to create something that look nice.
+
+This section is quite long, but don't be afraid,
+most of the code is some OpenGL boilerplate.
+If you just want to skim this section,
+here is a high level representation:
+
+ > - OpenGL Boilerplate
+ >
+ > - set some IORef (understand variables) for states
+ > - Drawing:
+ >
+ > - set doubleBuffer, handle depth, window size...
+ > - Use state to apply some transformations
+ >
+ > - Keyboard: hitting some key change the state of IORef
+ >
+ > - Generate 3D Object
+ >
+ > ~~~
+ > allPoints :: [ColoredPoint]
+ > allPoints =
+ > for all (x,y), -width Let z be the minimal depth such that
+ > mandel x y z > 0
+ > add the points
+ > (x, y, z,color)
+ > (x,-y, z,color)
+ > (x, y,-z,color)
+ > (x,-y,-z,color)
+ > + neighbors to make triangles
+ > ~~~
+
+
+
+
+
+We declare a new type `ExtComplex` (for extended complex).
+An extension of complex numbers with a third component:
+
+> data ExtComplex = C (GLfloat,GLfloat,GLfloat)
+> deriving (Show,Eq)
+> instance Num ExtComplex where
+> -- The shape of the 3D mandelbrot
+> -- will depend on this formula
+> C (x,y,z) * C (x',y',z') = C (x*x' - y*y' - z*z',
+> x*y' + y*x' + z*z',
+> x*z' + z*x' )
+> -- The rest is straightforward
+> fromInteger n = C (fromIntegral n, 0, 0)
+> C (x,y,z) + C (x',y',z') = C (x+x', y+y', z+z')
+> abs (C (x,y,z)) = C (sqrt (x*x + y*y + z*z), 0, 0)
+> signum (C (x,y,z)) = C (signum x, signum y, signum z)
+
+The most important part is the new multiplication instance.
+Modifying this formula will change radically the shape of the result.
+Here is the formula written in a more mathematical notation.
+I called the third component of these extended complex _strange_.
+
+$$ \mathrm{real} ((x,y,z) * (x',y',z')) = xx' - yy' - zz' $$
+
+$$ \mathrm{im} ((x,y,z) * (x',y',z')) = xy' - yx' + zz' $$
+
+$$ \mathrm{strange} ((x,y,z) * (x',y',z')) = xz' + zx' $$
+
+Note how if `z=z'=0` then the multiplication is the same to the complex one.
+
+
+
+> extcomplex :: GLfloat -> GLfloat -> GLfloat -> ExtComplex
+> extcomplex x y z = C (x,y,z)
+>
+> real :: ExtComplex -> GLfloat
+> real (C (x,y,z)) = x
+>
+> im :: ExtComplex -> GLfloat
+> im (C (x,y,z)) = y
+>
+> strange :: ExtComplex -> GLfloat
+> strange (C (x,y,z)) = z
+>
+> magnitude :: ExtComplex -> GLfloat
+> magnitude = real.abs
+
+
+
+ ### From 2D to 3D
+
+As we will use some 3D, we add some new directive in the boilerplate.
+But mainly, we simply state that will use some depth buffer.
+And also we will listen the keyboard.
+
+> main :: IO ()
+> main = do
+> -- GLUT need to be initialized
+> (progname,_) <- getArgsAndInitialize
+> -- We will use the double buffered mode (GL constraint)
+> -- We also Add the DepthBuffer (for 3D)
+> initialDisplayMode $=
+> [WithDepthBuffer,DoubleBuffered,RGBMode]
+> -- We create a window with some title
+> createWindow "3D HOpengGL Mandelbrot"
+> -- We add some directives
+> depthFunc $= Just Less
+> windowSize $= Size 500 500
+> -- Some state variables (I know it feels BAD)
+> angle <- newIORef ((35,0)::(GLfloat,GLfloat))
+> zoom <- newIORef (2::GLfloat)
+> campos <- newIORef ((0.7,0)::(GLfloat,GLfloat))
+> -- Function to call each frame
+> idleCallback $= Just idle
+> -- Function to call when keyboard or mouse is used
+> keyboardMouseCallback $=
+> Just (keyboardMouse angle zoom campos)
+> -- Each time we will need to update the display
+> -- we will call the function 'display'
+> -- But this time, we add some parameters
+> displayCallback $= display angle zoom campos
+> -- We enter the main loop
+> mainLoop
+
+The `idle` is here to change the states.
+There should never be any modification done in the `display` function.
+
+> idle = postRedisplay Nothing
+
+We introduce some helper function to manipulate
+standard `IORef`.
+Mainly `modVar x f` is equivalent to the imperative `x:=f(x)`,
+`modFst (x,y) (+1)` is equivalent to `(x,y) := (x+1,y)`
+and `modSnd (x,y) (+1)` is equivalent to `(x,y) := (x,y+1)`
+
+> modVar v f = do
+> v' <- get v
+> v $= (f v')
+> mapFst f (x,y) = (f x, y)
+> mapSnd f (x,y) = ( x,f y)
+
+And we use them to code the function handling keyboard.
+We will use the keys `hjkl` to rotate,
+`oi` to zoom and `sedf` to move.
+Also, hitting space will reset the view.
+Remember that `angle` and `campos` are pairs and `zoom` is a scalar.
+Also note `(+0.5)` is the function `\x->x+0.5`
+and `(-0.5)` is the number `-0.5` (yes I share your pain).
+
+> keyboardMouse angle zoom campos key state modifiers position =
+> -- We won't use modifiers nor position
+> kact angle zoom campos key state
+> where
+> -- reset view when hitting space
+> kact a z p (Char ' ') Down = do
+> a $= (0,0) -- angle
+> z $= 1 -- zoom
+> p $= (0,0) -- camera position
+> -- use of hjkl to rotate
+> kact a _ _ (Char 'h') Down = modVar a (mapFst (+0.5))
+> kact a _ _ (Char 'l') Down = modVar a (mapFst (+(-0.5)))
+> kact a _ _ (Char 'j') Down = modVar a (mapSnd (+0.5))
+> kact a _ _ (Char 'k') Down = modVar a (mapSnd (+(-0.5)))
+> -- use o and i to zoom
+> kact _ z _ (Char 'o') Down = modVar z (*1.1)
+> kact _ z _ (Char 'i') Down = modVar z (*0.9)
+> -- use sdfe to move the camera
+> kact _ _ p (Char 's') Down = modVar p (mapFst (+0.1))
+> kact _ _ p (Char 'f') Down = modVar p (mapFst (+(-0.1)))
+> kact _ _ p (Char 'd') Down = modVar p (mapSnd (+0.1))
+> kact _ _ p (Char 'e') Down = modVar p (mapSnd (+(-0.1)))
+> -- any other keys does nothing
+> kact _ _ _ _ _ = return ()
+
+Note `display` takes some parameters this time.
+This function if full of boilerplate:
+
+> display angle zoom position = do
+> -- set the background color (dark solarized theme)
+> clearColor $= Color4 0 0.1686 0.2117 1
+> clear [ColorBuffer,DepthBuffer]
+> -- Transformation to change the view
+> loadIdentity -- reset any transformation
+> -- tranlate
+> (x,y) <- get position
+> translate $ Vector3 x y 0
+> -- zoom
+> z <- get zoom
+> scale z z z
+> -- rotate
+> (xangle,yangle) <- get angle
+> rotate xangle $ Vector3 1.0 0.0 (0.0::GLfloat)
+> rotate yangle $ Vector3 0.0 1.0 (0.0::GLfloat)
+>
+> -- Now that all transformation were made
+> -- We create the object(s)
+> preservingMatrix drawMandelbrot
+>
+> swapBuffers -- refresh screen
+
+Not much to say about this function.
+Mainly there are two parts: apply some transformations, draw the object.
+
+ ### The 3D Mandelbrot
+
+We have finished with the OpenGL section, let's talk about how we
+generate the 3D points and colors.
+First, we will set the number of details to 200 pixels in the three dimensions.
+
+> nbDetails = 200 :: GLfloat
+> width = nbDetails
+> height = nbDetails
+> deep = nbDetails
+
+This time, instead of just drawing some line or some group of points,
+we will show triangles.
+The function `allPoints` will provide a multiple of three points.
+Each three successive point representing the coordinate of each vertex of a triangle.
+
+
+> drawMandelbrot = do
+> -- We will print Points (not triangles for example)
+> renderPrimitive Triangles $ do
+> mapM_ drawColoredPoint allPoints
+> where
+> drawColoredPoint (x,y,z,c) = do
+> color c
+> vertex $ Vertex3 x y z
+
+In fact, we will provide six ordered points.
+These points will be used to draw two triangles.
+
+blogimage("triangles.png","Explain triangles")
+
+The next function is a bit long.
+Here is an approximative English version:
+
+~~~
+forall x from -width to width
+ forall y from -height to height
+ forall the neighbors of (x,y)
+ let z be the smalled depth such that (mandel x y z)>0
+ let c be the color given by mandel x y z
+ add the point corresponding to (x,y,z,c)
+~~~
+
+Also, I added a test to hide points too far from the border.
+In fact, this function show points close to the surface of the modified mandelbrot set. But not the mandelbrot set itself.
+
+
+depthPoints :: [ColoredPoint]
+depthPoints = do
+ x <- [-width..width]
+ y <- [-height..height]
+ let
+ depthOf x' y' = maxZeroIndex (mandel x' y') 0 deep logdeep
+ logdeep = floor ((log deep) / log 2)
+ z1 = depthOf x y
+ z2 = depthOf (x+1) y
+ z3 = depthOf (x+1) (y+1)
+ z4 = depthOf x (y+1)
+ c1 = mandel x y (z1+1)
+ c2 = mandel (x+1) y (z2+1)
+ c3 = mandel (x+1) (y+1) (z3+1)
+ c4 = mandel x (y+1) (z4+1)
+ p1 = ( x /width, y /height, z1/deep, colorFromValue c1)
+ p2 = ((x+1)/width, y /height, z2/deep, colorFromValue c2)
+ p3 = ((x+1)/width,(y+1)/height, z3/deep, colorFromValue c3)
+ p4 = ( x /width,(y+1)/height, z4/deep, colorFromValue c4)
+ if (and $ map (>=57) [c1,c2,c3,c4])
+ then []
+ else [p1,p2,p3,p1,p3,p4]
+
+
+If you look at the function above, you see a lot of common patterns.
+Haskell is very efficient to make this better.
+Here is a harder to read but shorter and more generic rewritten function:
+
+> depthPoints :: [ColoredPoint]
+> depthPoints = do
+> x <- [-width..width]
+> y <- [-height..height]
+> let
+> neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)]
+> depthOf (u,v) = maxZeroIndex (mandel u v) 0 deep logdeep
+> logdeep = floor ((log deep) / log 2)
+> -- zs are 3D points with found depth
+> zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors
+> -- ts are 3D pixels + mandel value
+> ts = map (\(u,v,w) -> (u,v,w,mandel u v (w+1))) zs
+> -- ps are 3D opengl points + color value
+> ps = map (\(u,v,w,c') ->
+> (u/width,v/height,w/deep,colorFromValue c')) ts
+> -- If the point diverged too fast, don't display it
+> if (and $ map (\(_,_,_,c) -> c>=57) ts)
+> then []
+> -- Draw two triangles
+> else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3]
+
+If you prefer the first version, then just imagine how hard it will be to change the enumeration of the point from (x,y) to (x,z) for example.
+
+Also, we didn't searched for negative values.
+This modified Mandelbrot is no more symmetric relatively to the plan `y=0`.
+But it is symmetric relatively to the plan `z=0`.
+Then I mirror these values.
+
+> allPoints :: [ColoredPoint]
+> allPoints = planPoints ++ map inverseDepth planPoints
+> where
+> planPoints = depthPoints
+> inverseDepth (x,y,z,c) = (x,y,-z+1/deep,c)
+
+The rest of the program is very close to the preceding one.
+
+
+
+> -- given f min max nbtest,
+> -- considering
+> -- - f is an increasing function
+> -- - f(min)=0
+> -- - f(max)≠0
+> -- then maxZeroIndex f min max nbtest returns x such that
+> -- f(x - ε)=0 and f(x + ε)≠0
+> -- where ε=(max-min)/2^(nbtest+1)
+> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) =>
+> (a -> b) -> a -> a -> Int -> a
+> maxZeroIndex func minval maxval 0 = (minval+maxval)/2
+> maxZeroIndex func minval maxval n =
+> if (func medpoint) /= 0
+> then maxZeroIndex func minval medpoint (n-1)
+> else maxZeroIndex func medpoint maxval (n-1)
+> where medpoint = (minval+maxval)/2
+
+I made the color slightly brighter
+
+> colorFromValue n =
+> let
+> t :: Int -> GLfloat
+> t i = 0.7 + 0.3*cos( fromIntegral i / 10 )
+> in
+> Color3 (t n) (t (n+5)) (t (n+10))
+
+We only changed from `Complex` to `ExtComplex` of the main `f` function.
+
+> f :: ExtComplex -> ExtComplex -> Int -> Int
+> f c z 0 = 0
+> f c z n = if (magnitude z > 2 )
+> then n
+> else f c ((z*z)+c) (n-1)
+
+
+
+We simply add a new dimension to the `mandel` function
+and change the type signature of `f` from `Complex` to `ExtComplex`.
+
+> mandel x y z =
+> let r = 2.0 * x / width
+> i = 2.0 * y / height
+> s = 2.0 * z / deep
+> in
+> f (extcomplex r i s) 0 64
+
+
+Here is the result:
+
+blogimage("mandelbrot_3D.png","A 3D mandelbrot like")
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/ExtComplex.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/ExtComplex.hs
new file mode 100644
index 0000000..caba8d0
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/ExtComplex.hs
@@ -0,0 +1,37 @@
+module ExtComplex where
+
+import Graphics.Rendering.OpenGL
+
+-- This time I use unpacked strict data type
+-- Far faster when compiled.
+data ExtComplex = C {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ deriving (Show,Eq)
+
+instance Num ExtComplex where
+ -- The shape of the 3D mandelbrot
+ -- will depend on this formula
+ (C x y z) * (C x' y' z') = C (x*x' - y*y' - z*z')
+ (x*y' + y*x' + z*z')
+ (x*z' + z*x' )
+ -- The rest is straightforward
+ fromInteger n = C (fromIntegral n) 0 0
+ (C x y z) + (C x' y' z') = C (x+x') (y+y') (z+z')
+ abs (C x y z) = C (sqrt (x*x + y*y + z*z)) 0 0
+ signum (C x y z) = C (signum x) (signum y) (signum z)
+
+extcomplex :: GLfloat -> GLfloat -> GLfloat -> ExtComplex
+extcomplex x y z = C x y z
+
+real :: ExtComplex -> GLfloat
+real (C x _ _) = x
+
+im :: ExtComplex -> GLfloat
+im (C _ y _) = y
+
+strange :: ExtComplex -> GLfloat
+strange (C _ _ z) = z
+
+magnitude :: ExtComplex -> GLfloat
+magnitude = real.abs
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandel.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandel.hs
new file mode 100644
index 0000000..9500e0b
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandel.hs
@@ -0,0 +1,13 @@
+-- The Mandelbrot function
+module Mandel (mandel) where
+
+import ExtComplex
+
+mandel r i s nbIterations =
+ f (extcomplex r i s) 0 nbIterations
+ where
+ f :: ExtComplex -> ExtComplex -> Int -> Int
+ f c z 0 = 0
+ f c z n = if (magnitude z > 2 )
+ then n
+ else f c ((z*z)+c) (n-1)
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs
new file mode 100644
index 0000000..d9fb813
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/Mandelbulb.lhs
@@ -0,0 +1,90 @@
+ ## Naïve code cleaning
+
+The first approach to clean the code is to separate the GLUT/OpenGL
+part from the computation of the shape.
+Here is the cleaned version of the preceding section.
+Most boilerplate was put in external files.
+
+- [`YBoiler.hs`](code/04_Mandelbulb/YBoiler.hs), the 3D rendering
+- [`Mandel`](code/04_Mandelbulb/Mandel.hs), the mandel function
+- [`ExtComplex`](code/04_Mandelbulb/ExtComplex.hs), the extended complexes
+
+> import YBoiler -- Most the OpenGL Boilerplate
+> import Mandel -- The 3D Mandelbrot maths
+
+The `yMainLoop` takes two arguments:
+the title of the window
+and a function from time to triangles
+
+> main :: IO ()
+> main = yMainLoop "3D Mandelbrot" (\_ -> allPoints)
+
+We set some global constant (this is generally bad).
+
+> nbDetails = 200 :: GLfloat
+> width = nbDetails
+> height = nbDetails
+> deep = nbDetails
+
+We then generate colored points from our function.
+This is similar to the preceding section.
+
+> allPoints :: [ColoredPoint]
+> allPoints = planPoints ++ map inverseDepth planPoints
+> where
+> planPoints = depthPoints ++ map inverseHeight depthPoints
+> inverseHeight (x,y,z,c) = (x,-y,z,c)
+> inverseDepth (x,y,z,c) = (x,y,-z+1/deep,c)
+
+> depthPoints :: [ColoredPoint]
+> depthPoints = do
+> x <- [-width..width]
+> y <- [0..height]
+> let
+> neighbors = [(x,y),(x+1,y),(x+1,y+1),(x,y+1)]
+> depthOf (u,v) = maxZeroIndex (ymandel u v) 0 deep 7
+> -- zs are 3D points with found depth
+> zs = map (\(u,v) -> (u,v,depthOf (u,v))) neighbors
+> -- ts are 3D pixels + mandel value
+> ts = map (\(u,v,w) -> (u,v,w,ymandel u v (w+1))) zs
+> -- ps are 3D opengl points + color value
+> ps = map (\(u,v,w,c') ->
+> (u/width,v/height,w/deep,colorFromValue c')) ts
+> -- If the point diverged too fast, don't display it
+> if (and $ map (\(_,_,_,c) -> c>=57) ts)
+> then []
+> -- Draw two triangles
+> else [ps!!0,ps!!1,ps!!2,ps!!0,ps!!2,ps!!3]
+>
+>
+> -- given f min max nbtest,
+> -- considering
+> -- - f is an increasing function
+> -- - f(min)=0
+> -- - f(max)≠0
+> -- then maxZeroIndex f min max nbtest returns x such that
+> -- f(x - ε)=0 and f(x + ε)≠0
+> -- where ε=(max-min)/2^(nbtest+1)
+> maxZeroIndex func minval maxval 0 = (minval+maxval)/2
+> maxZeroIndex func minval maxval n =
+> if (func medpoint) /= 0
+> then maxZeroIndex func minval medpoint (n-1)
+> else maxZeroIndex func medpoint maxval (n-1)
+> where medpoint = (minval+maxval)/2
+>
+> colorFromValue n =
+> let
+> t :: Int -> GLfloat
+> t i = 0.7 + 0.3*cos( fromIntegral i / 10 )
+> in
+> ((t n),(t (n+5)),(t (n+10)))
+>
+> ymandel x y z = mandel (2*x/width) (2*y/height) (2*z/deep) 64
+
+This code is cleaner but many things doesn't feel right.
+First, all the user interaction code is outside our main file.
+I feel it is okay to hide the detail for the rendering.
+But I would have preferred to control the user actions.
+
+On the other hand, we continue to handle a lot rendering details.
+For example, we provide ordered vertices.
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/YBoiler.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/YBoiler.hs
new file mode 100644
index 0000000..867f8b7
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/04_Mandelbulb/YBoiler.hs
@@ -0,0 +1,120 @@
+-- An OpenGL boilerplate
+module YBoiler (GLfloat,yMainLoop,ColoredPoint,Color3) where
+
+import Graphics.Rendering.OpenGL
+import Graphics.UI.GLUT
+import Data.IORef
+
+
+type ColorRGB = (GLfloat,GLfloat,GLfloat)
+type YAngle = (GLfloat,GLfloat,GLfloat)
+type ColoredPoint = (GLfloat,GLfloat,GLfloat,ColorRGB)
+
+yMainLoop :: String -> (Int -> [ColoredPoint]) -> IO ()
+yMainLoop windowTitle triangles = do
+ -- GLUT need to be initialized
+ (progname,_) <- getArgsAndInitialize
+ -- We will use the double buffered mode (GL constraint)
+ -- We also Add the DepthBuffer (for 3D)
+ initialDisplayMode $=
+ [WithDepthBuffer,DoubleBuffered,RGBMode]
+ -- We create a window with some title
+ createWindow windowTitle
+ -- We add some directives
+ depthFunc $= Just Less
+ -- matrixMode $= Projection
+ windowSize $= Size 500 500
+ -- Some state variables (I know it feels BAD)
+ angle <- newIORef ((35,0,0)::YAngle)
+ zoom <- newIORef (2::GLfloat)
+ campos <- newIORef ((0.7,0)::(GLfloat,GLfloat))
+ -- Action to call when waiting
+ idleCallback $= Just idle
+ -- We will use the keyboard
+ keyboardMouseCallback $=
+ Just (keyboardMouse angle zoom campos)
+ -- Each time we will need to update the display
+ -- we will call the function 'display'
+ -- But this time, we add some parameters
+ displayCallback $= display angle zoom campos triangles
+ -- We enter the main loop
+ mainLoop
+
+idle = postRedisplay Nothing
+
+-- modify IORef cleanly
+modVar :: IORef a -> (a -> a) -> IO ()
+modVar v f = do
+ v' <- get v
+ v $= (f v')
+-- modify IORef (a,b) using f:a->a
+mapFst f (x,y) = (f x, y)
+-- modify IORef (a,b) using f:b->b
+mapSnd f (x,y) = ( x,f y)
+mapFst3 f (x,y,z) = (f x, y, z)
+mapSnd3 f (x,y,z) = (x, f y, z)
+mapThi3 f (x,y,z) = (x, y, f z)
+
+-- Get User Input
+keyboardMouse angle zoom pos key state modifiers position =
+ kact angle zoom pos key state
+ where
+ -- reset view when hitting space
+ kact a z p (Char ' ') Down = do
+ a $= (0,0,0)
+ z $= 1
+ p $= (0,0)
+ -- use of hjkl to rotate
+ kact a _ _ (Char 'j') Down = modVar a (mapFst3 (+0.5))
+ kact a _ _ (Char 'l') Down = modVar a (mapFst3 (+(-0.5)))
+ kact a _ _ (Char 'i') Down = modVar a (mapSnd3 (+0.5))
+ kact a _ _ (Char 'k') Down = modVar a (mapSnd3 (+(-0.5)))
+ kact a _ _ (Char 'o') Down = modVar a (mapThi3 (+0.5))
+ kact a _ _ (Char 'u') Down = modVar a (mapThi3 (+(-0.5)))
+ -- use o and i to zoom
+ kact _ s _ (Char '+') Down = modVar s (*1.1)
+ kact _ s _ (Char '-') Down = modVar s (*0.9)
+ -- use sdfe to move the camera
+ kact _ _ p (Char 's') Down = modVar p (mapFst (+0.1))
+ kact _ _ p (Char 'f') Down = modVar p (mapFst (+(-0.1)))
+ kact _ _ p (Char 'd') Down = modVar p (mapSnd (+0.1))
+ kact _ _ p (Char 'e') Down = modVar p (mapSnd (+(-0.1)))
+ -- any other keys does nothing
+ kact _ _ _ _ _ = return ()
+
+-- The function that will display datas
+display angle zoom position triangles = do
+ -- set the background color (dark solarized theme)
+ clearColor $= Color4 0 0.1686 0.2117 1
+ clear [ColorBuffer,DepthBuffer]
+ -- Transformation to change the view
+ loadIdentity -- reset any transformation
+ -- tranlate
+ (x,y) <- get position
+ translate $ Vector3 x y 0
+ -- zoom
+ z <- get zoom
+ scale z z z
+ -- rotate
+ (xangle,yangle,zangle) <- get angle
+ rotate xangle $ Vector3 1.0 0.0 (0.0::GLfloat)
+ rotate yangle $ Vector3 0.0 1.0 (0.0::GLfloat)
+ rotate zangle $ Vector3 0.0 0.0 (1.0::GLfloat)
+ -- Now that all transformation were made
+ -- We create the object(s)
+ t <- get elapsedTime
+ preservingMatrix $ drawObject (triangles t)
+ swapBuffers -- refresh screen
+
+red (r,_,_) = r
+green (_,g,_) = g
+blue (_,_,b) = b
+
+drawObject triangles = do
+ -- We will print Points (not triangles for example)
+ renderPrimitive Triangles $ do
+ mapM_ drawColoredPoint triangles
+ where
+ drawColoredPoint (x,y,z,c) = do
+ color $ Color3 (red c) (green c) (blue c)
+ vertex $ Vertex3 x y z
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/ExtComplex.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/ExtComplex.hs
new file mode 100644
index 0000000..caba8d0
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/ExtComplex.hs
@@ -0,0 +1,37 @@
+module ExtComplex where
+
+import Graphics.Rendering.OpenGL
+
+-- This time I use unpacked strict data type
+-- Far faster when compiled.
+data ExtComplex = C {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ deriving (Show,Eq)
+
+instance Num ExtComplex where
+ -- The shape of the 3D mandelbrot
+ -- will depend on this formula
+ (C x y z) * (C x' y' z') = C (x*x' - y*y' - z*z')
+ (x*y' + y*x' + z*z')
+ (x*z' + z*x' )
+ -- The rest is straightforward
+ fromInteger n = C (fromIntegral n) 0 0
+ (C x y z) + (C x' y' z') = C (x+x') (y+y') (z+z')
+ abs (C x y z) = C (sqrt (x*x + y*y + z*z)) 0 0
+ signum (C x y z) = C (signum x) (signum y) (signum z)
+
+extcomplex :: GLfloat -> GLfloat -> GLfloat -> ExtComplex
+extcomplex x y z = C x y z
+
+real :: ExtComplex -> GLfloat
+real (C x _ _) = x
+
+im :: ExtComplex -> GLfloat
+im (C _ y _) = y
+
+strange :: ExtComplex -> GLfloat
+strange (C _ _ z) = z
+
+magnitude :: ExtComplex -> GLfloat
+magnitude = real.abs
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandel.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandel.hs
new file mode 100644
index 0000000..7c1ef34
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandel.hs
@@ -0,0 +1,14 @@
+-- The Mandelbrot function
+module Mandel (mandel) where
+
+import ExtComplex
+
+mandel :: Float -> Float -> Float -> Int -> Int
+mandel r i s nbIterations =
+ f (extcomplex r i s) 0 nbIterations
+ where
+ f :: ExtComplex -> ExtComplex -> Int -> Int
+ f _ _ 0 = 0
+ f c z n = if (magnitude z > 2 )
+ then n
+ else f c ((z*z)+c) (n-1)
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs
new file mode 100644
index 0000000..f58abff
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/Mandelbulb.lhs
@@ -0,0 +1,250 @@
+ ## Functional organization?
+
+Some points:
+
+1. OpenGL and GLUT is done in C.
+ In particular the `mainLoop` function is a direct link to the C library (FFI).
+ This function is clearly far from the functional paradigm.
+ Could we make this better?
+ We will have two choices:
+
+ - create our own `mainLoop` function to make it more functional.
+ - deal with the imperative nature of the GLUT `mainLoop` function.
+
+ As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the `mainLoop` function.
+2. Our main problem come from user interaction.
+ If you ask "the Internet",
+ about how to deal with user interaction with a functional paradigm,
+ the main answer is to use _functional reactive programming_ (FRP).
+ I won't use FRP in this article.
+ Instead, I'll use a simpler while less effective way to deal with user interaction.
+ But The method I'll use will be as pure and functional as possible.
+
+Here is how I imagine things should go.
+First, what the main loop should look like if we could make our own:
+
+
+functionalMainLoop =
+ Read user inputs and provide a list of actions
+ Apply all actions to the World
+ Display one frame
+ repetere aeternum
+
+
+Clearly, ideally we should provide only three parameters to this main loop function:
+
+- an initial World state
+- a mapping between the user interactions and functions which modify the world
+- a function taking two parameters: time and world state and render a new world without user interaction.
+
+Here is a real working code, I've hidden most display functions.
+The YGL, is a kind of framework to display 3D functions.
+But it can easily be extended to many kind of representation.
+
+> import YGL -- Most the OpenGL Boilerplate
+> import Mandel -- The 3D Mandelbrot maths
+
+We first set the mapping between user input and actions.
+The type of each couple should be of the form
+`(user input, f)` where (in a first time) `f:World -> World`.
+It means, the user input will transform the world state.
+
+> -- Centralize all user input interaction
+> inputActionMap :: InputMap World
+> inputActionMap = inputMapFromList [
+> (Press 'k' , rotate xdir 5)
+> ,(Press 'i' , rotate xdir (-5))
+> ,(Press 'j' , rotate ydir 5)
+> ,(Press 'l' , rotate ydir (-5))
+> ,(Press 'o' , rotate zdir 5)
+> ,(Press 'u' , rotate zdir (-5))
+> ,(Press 'f' , translate xdir 0.1)
+> ,(Press 's' , translate xdir (-0.1))
+> ,(Press 'e' , translate ydir 0.1)
+> ,(Press 'd' , translate ydir (-0.1))
+> ,(Press 'z' , translate zdir 0.1)
+> ,(Press 'r' , translate zdir (-0.1))
+> ,(Press '+' , zoom 1.1)
+> ,(Press '-' , zoom (1/1.1))
+> ,(Press 'h' , resize 1.2)
+> ,(Press 'g' , resize (1/1.2))
+> ]
+
+And of course a type design the World State.
+The important part is that it is our World State type.
+We could have used any kind of data type.
+
+> -- I prefer to set my own name for these types
+> data World = World {
+> angle :: Point3D
+> , scale :: Scalar
+> , position :: Point3D
+> , shape :: Scalar -> Function3D
+> , box :: Box3D
+> , told :: Time -- last frame time
+> }
+
+The important part to glue our own type to the framework
+is to make our type an instance of the type class `DisplayableWorld`.
+We simply have to provide the definition of some functions.
+
+> instance DisplayableWorld World where
+> winTitle _ = "The YGL Mandelbulb"
+> camera w = Camera {
+> camPos = position w,
+> camDir = angle w,
+> camZoom = scale w }
+> -- objects for world w
+> -- is the list of one unique element
+> -- The element is an YObject
+> -- more precisely the XYFunc Function3D Box3D
+> -- where the Function3D is the type
+> -- Point -> Point -> Maybe (Point,Color)
+> -- and its value here is ((shape w) res)
+> -- and the Box3D value is defbox
+> objects w = [XYFunc ((shape w) res) defbox]
+> where
+> res = resolution $ box w
+> defbox = box w
+
+The `camera` function will retrieve an object of type `Camera` which contains
+most necessary information to set our camera.
+The `objects` function will returns a list of objects.
+Their type is `YObject`. Note the generation of triangles is no more in this file.
+Until here we only used declarative pattern.
+
+We also need to set all our transformation functions.
+These function are used to update the world state.
+
+> xdir :: Point3D
+> xdir = makePoint3D (1,0,0)
+> ydir :: Point3D
+> ydir = makePoint3D (0,1,0)
+> zdir :: Point3D
+> zdir = makePoint3D (0,0,1)
+
+Note `(-*<)` is the scalar product (`α -*< (x,y,z) = (αx,αy,αz)`).
+Also note we could add two Point3D.
+
+> rotate :: Point3D -> Scalar -> World -> World
+> rotate dir angleValue world =
+> world {
+> angle = (angle world) + (angleValue -*< dir) }
+>
+> translate :: Point3D -> Scalar -> World -> World
+> translate dir len world =
+> world {
+> position = (position world) + (len -*< dir) }
+>
+> zoom :: Scalar -> World -> World
+> zoom z world = world {
+> scale = z * scale world }
+>
+> resize :: Scalar -> World -> World
+> resize r world = world {
+> box = (box world) {
+> resolution = sqrt ((resolution (box world))**2 * r) }}
+
+The resize is used to generate the 3D function.
+As I wanted the time spent to generate a more detailed view
+to grow linearly I use this not so straightforward formula.
+
+The `yMainLoop` takes three arguments.
+
+- A map between user Input and world transformation
+- A timed world transformation
+- An initial world state
+
+> main :: IO ()
+> main = yMainLoop inputActionMap idleAction initialWorld
+
+Here is our initial world state.
+
+> -- We initialize the world state
+> -- then angle, position and zoom of the camera
+> -- And the shape function
+> initialWorld :: World
+> initialWorld = World {
+> angle = makePoint3D (-30,-30,0)
+> , position = makePoint3D (0,0,0)
+> , scale = 0.8
+> , shape = shapeFunc
+> , box = Box3D { minPoint = makePoint3D (-2,-2,-2)
+> , maxPoint = makePoint3D (2,2,2)
+> , resolution = 0.16 }
+> , told = 0
+> }
+
+We will define `shapeFunc` later.
+Here is the function which transform the world even without user action.
+Mainly it makes some rotation.
+
+> idleAction :: Time -> World -> World
+> idleAction tnew world = world {
+> angle = (angle world) + (delta -*< zdir)
+> , told = tnew
+> }
+> where
+> anglePerSec = 5.0
+> delta = anglePerSec * elapsed / 1000.0
+> elapsed = fromIntegral (tnew - (told world))
+
+Now the function which will generate points in 3D.
+The first parameter (`res`) is the resolution of the vertex generation.
+More precisely, `res` is distance between two points on one direction.
+We need it to "close" our shape.
+
+The type `Function3D` is `Point -> Point -> Maybe Point`.
+Because we consider partial functions
+(for some `(x,y)` our function can be undefined).
+
+> shapeFunc :: Scalar -> Function3D
+> shapeFunc res x y =
+> let
+> z = maxZeroIndex (ymandel x y) 0 1 20
+> in
+> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 |
+> val <- [res], xeps <- [-val,val], yeps<-[-val,val]]
+> then Nothing
+> else Just (z,colorFromValue ((ymandel x y z) * 64))
+
+With the color function.
+
+> colorFromValue :: Point -> Color
+> colorFromValue n =
+> let
+> t :: Point -> Scalar
+> t i = 0.7 + 0.3*cos( i / 10 )
+> in
+> makeColor (t n) (t (n+5)) (t (n+10))
+
+The rest is similar to the preceding sections.
+
+> -- given f min max nbtest,
+> -- considering
+> -- - f is an increasing function
+> -- - f(min)=0
+> -- - f(max)≠0
+> -- then maxZeroIndex f min max nbtest returns x such that
+> -- f(x - ε)=0 and f(x + ε)≠0
+> -- where ε=(max-min)/2^(nbtest+1)
+> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) =>
+> (a -> b) -> a -> a -> Int -> a
+> maxZeroIndex _ minval maxval 0 = (minval+maxval)/2
+> maxZeroIndex func minval maxval n =
+> if (func medpoint) /= 0
+> then maxZeroIndex func minval medpoint (n-1)
+> else maxZeroIndex func medpoint maxval (n-1)
+> where medpoint = (minval+maxval)/2
+>
+> ymandel :: Point -> Point -> Point -> Point
+> ymandel x y z = fromIntegral (mandel x y z 64) / 64
+
+I won't explain how the magic occurs here.
+If you are interested, just read the file [`YGL.hs`](code/05_Mandelbulb/YGL.hs).
+It is commented a lot.
+
+- [`YGL.hs`](code/05_Mandelbulb/YGL.hs), the 3D rendering framework
+- [`Mandel`](code/05_Mandelbulb/Mandel.hs), the mandel function
+- [`ExtComplex`](code/05_Mandelbulb/ExtComplex.hs), the extended complexes
+
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs
new file mode 100644
index 0000000..2f6e166
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/05_Mandelbulb/YGL.hs
@@ -0,0 +1,383 @@
+{-
+The module YGL will contains most boilerplate
+And display details.
+
+To make things even nicer, we should separate
+this file in many different parts.
+Typically separate the display function.
+
+-}
+module YGL (
+ -- Here is declared our interface with external files
+ -- that will include our YGL module
+
+ -- Declarations related to data types
+ Point -- the 1 dimension point type
+ , Time -- the type for the time
+ , Scalar -- the type for scalar values
+ , Color -- the type for color (3 scalars)
+ , Point3D (..) -- A 3D point type (3 Points)
+ , makePoint3D -- helper (x,y,z) -> Point3D
+ , (-*<) -- scalar product on Point3D a -*< (x,y,z) = (ax,ay,az)
+ , Function3D -- Point -> Point -> Maybe (Point,Color)
+
+ -- Your world state must be an instance
+ -- of the DisplayableWorld type class
+ , DisplayableWorld (..)
+ -- Datas related to DisplayableWorld
+ , Camera (..)
+ , YObject (..) -- 3D Objects to display
+ , Box3D (..) -- Some bounded 3D box
+ , makeBox -- helper to make a box
+ , hexColor -- Color from hexadecimal string
+ , makeColor -- make color from RGB values
+ -- Interface related to user input
+ , InputMap
+ , UserInput (Press,Ctrl,Alt,CtrlAlt)
+ , inputMapFromList
+
+ -- The main loop function to call
+ , yMainLoop
+) where
+
+-- A bunch of imports
+import Numeric (readHex) -- to read hexadecimal values
+
+-- Import of OpenGL and GLUT
+-- but, I use my own Color type, therefore I hide the definition
+-- of Color inside GLUT and OpenGL packages
+import Graphics.Rendering.OpenGL hiding (Color)
+import Graphics.UI.GLUT hiding (Color)
+import Data.IORef
+
+-- I use Map to deal with user interaction
+import qualified Data.Map as Map
+
+-- Some standard stuff
+import Control.Monad (when)
+import Data.Maybe (isNothing)
+
+{-- Things start to be complex here.
+- Just take the time to follow me.
+--}
+
+-- | A 1D point
+type Point = GLfloat
+-- | A Scalar value
+type Scalar = GLfloat
+-- | The time type (currently its Int)
+type Time = Int
+-- | A 3D Point mainly '(x,y,z)'
+data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read)
+type Color = Color3 Scalar
+
+-- Get x (resp. y, z) coordinate of a 3D point
+xpoint :: Point3D -> Point
+xpoint (P (x,_,_)) = x
+ypoint :: Point3D -> Point
+ypoint (P (_,y,_)) = y
+zpoint :: Point3D -> Point
+zpoint (P (_,_,z)) = z
+
+-- Create a Point3D element from a triplet
+makePoint3D :: (Point,Point,Point) -> Point3D
+makePoint3D = P
+
+-- Make Point3D an instance of Num
+instance Num Point3D where
+ (+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz)
+ (-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz)
+ (*) (P (ax,ay,az)) (P (bx,by,bz)) = P ( ay*bz - az*by
+ , az*bx - ax*bz
+ , ax*by - ay*bx )
+ abs (P (x,y,z)) = P (abs x,abs y, abs z)
+ signum (P (x,y,z)) = P (signum x, signum y, signum z)
+ fromInteger i = P (fromInteger i, 0, 0)
+
+-- The scalar product
+infixr 5 -*<
+(-*<) :: Scalar -> Point3D -> Point3D
+(-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p)
+
+-- Used internally to convert point3D to different types
+toGLVector3 :: Point3D -> Vector3 GLfloat
+toGLVector3 (P(x,y,z)) = Vector3 x y z
+
+toGLVertex3 :: Point3D -> Vertex3 GLfloat
+toGLVertex3 (P(x,y,z)) = Vertex3 x y z
+
+toGLNormal3 :: Point3D -> Normal3 GLfloat
+toGLNormal3 (P(x,y,z)) = Normal3 x y z
+
+-- | The Box3D type represent a 3D bounding box
+-- | Note if minPoint = (x,y,z) and maxPoint = (x',y',z')
+-- | Then to have a non empty box you must have
+-- | x (Point,Point,Point) -> Scalar -> Box3D
+makeBox mini maxi res = Box3D {
+ minPoint = makePoint3D mini
+ , maxPoint = makePoint3D maxi
+ , resolution = res }
+
+-- | A Triangle3D is simply 3 points and a color
+type Triangle3D = (Point3D,Point3D,Point3D,Color)
+
+-- | The type Atom is the atom for our display here we'll only use triangles.
+-- | For a general purpose library we should add many other different atoms
+-- | corresponding to Quads for example.
+data Atom = ColoredTriangle Triangle3D
+
+-- | A Function3D is simply a function for each x,y associate a z and a color
+-- | If undefined at point (x,y), it returns Nothing.
+type Function3D = Point -> Point -> Maybe (Point,Color)
+
+-- | Our objects that will be displayed
+-- | Wether a function3D delimited by a Box
+-- | or a list of Atoms
+data YObject = XYFunc Function3D Box3D
+ | Atoms [Atom]
+
+-- | The function atoms retrieve the list of atoms from an YObject
+atoms :: YObject -> [Atom]
+atoms (XYFunc f b) = getObject3DFromShapeFunction f b
+atoms (Atoms atomList) = atomList
+
+-- | We decalre the input map type we need here
+-- | It is our API
+-- | I don't use Mouse but it can be easily added
+type InputMap worldType = Map.Map UserInput (worldType -> worldType)
+data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char
+ deriving (Eq,Ord,Show,Read)
+
+-- | A displayable world is a type for which
+-- | ther exists a function that provide sufficient informations
+-- | to provide a camera, lights, objects and a window title.
+class DisplayableWorld world where
+ camera :: world -> Camera
+ camera _ = defaultCamera
+ lights :: world -> [Light]
+ lights _ = []
+ objects :: world -> [YObject]
+ objects _ = []
+ winTitle :: world -> String
+ winTitle _ = "YGL"
+
+-- | the Camera type to know how to
+-- | Transform the scene to see the right view.
+data Camera = Camera {
+ camPos :: Point3D
+ , camDir :: Point3D
+ , camZoom :: Scalar }
+
+-- | A default initial camera
+defaultCamera :: Camera
+defaultCamera = Camera {
+ camPos = makePoint3D (0,0,0)
+ , camDir = makePoint3D (0,0,0)
+ , camZoom = 1 }
+
+
+-- | Given a shape function and a delimited Box3D
+-- | return a list of Atoms (here only colored triangles) to be displayed
+getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom]
+getObject3DFromShapeFunction shape box = do
+ x <- [xmin,xmin+res..xmax]
+ y <- [ymin,ymin+res..ymax]
+ let
+ neighbors = [(x,y),(x+res,y),(x+res,y+res),(x,y+res)]
+ -- zs are 3D points with found depth and color
+ -- zs :: [ (Point,Point,Point,Maybe (Point,Color) ]
+ zs = map (\(u,v) -> (u,v,shape u v)) neighbors
+ -- ps are 3D opengl points + color value
+ ps = zs
+ -- If the point diverged too fast, don't display it
+ if any (\(_,_,z) -> isNothing z) zs
+ then []
+ -- Draw two triangles
+ -- 3 - 2
+ -- | / |
+ -- 0 - 1
+ -- The order is important
+ else
+ [ makeAtom (ps!!0) (ps!!2) (ps!!1)
+ , makeAtom (ps!!0) (ps!!3) (ps!!2) ]
+ where
+ makeAtom (p0x,p0y,Just (p0z,c0)) (p1x,p1y,Just (p1z,_)) (p2x,p2y,Just (p2z,_)) =
+ ColoredTriangle (makePoint3D (p0x,p0y,p0z)
+ ,makePoint3D (p1x,p1y,p1z)
+ ,makePoint3D (p2x,p2y,p2z)
+ ,c0)
+ makeAtom _ _ _ = error "Somethings wrong here"
+
+ -- some naming to make it
+ -- easier to read
+ xmin = xpoint $ minPoint box
+ xmax = xpoint $ maxPoint box
+ ymin = ypoint $ minPoint box
+ ymax = ypoint $ maxPoint box
+ res = resolution box
+
+-- | Get the user input map from a list
+inputMapFromList :: (DisplayableWorld world) =>
+ [(UserInput,world -> world)] -> InputMap world
+inputMapFromList = Map.fromList
+
+{--
+- We set our mainLoop function
+- As you can see the code is _not_ pure
+- and not even functionnal friendly!
+- But when called,
+- it will look like a pure functional function.
+--}
+yMainLoop :: (DisplayableWorld worldType) =>
+ -- the mapping user input / world
+ InputMap worldType
+ -- function that modify the world
+ -> (Time -> worldType -> worldType)
+ -- the world state of type worldType
+ -> worldType
+ -- into IO () for obvious reason
+ -> IO ()
+yMainLoop inputActionMap
+ worldTranformer
+ world = do
+ -- The boilerplate
+ _ <- getArgsAndInitialize
+ initialDisplayMode $=
+ [WithDepthBuffer,DoubleBuffered,RGBMode]
+ _ <- createWindow $ winTitle world
+ depthFunc $= Just Less
+ windowSize $= Size 500 500
+ -- The state variables for the world (I know it feels BAD)
+ worldRef <- newIORef world
+ -- Action to call when waiting
+ idleCallback $= Just (idle worldTranformer worldRef)
+ -- the keyboard will update the world
+ keyboardMouseCallback $=
+ Just (keyboardMouse inputActionMap worldRef)
+ -- We generate one frame using the callback
+ displayCallback $= display worldRef
+ -- let OpenGL resize normal vectors to unity
+ normalize $= Enabled
+ shadeModel $= Smooth
+ -- Lights (in a better version should be put elsewhere)
+ lighting $= Enabled
+ ambient (Light 0) $= Color4 0 0 0 1
+ diffuse (Light 0) $= Color4 0.5 0.5 0.5 1
+ specular (Light 0) $= Color4 1 1 1 1
+ position (Light 0) $= Vertex4 1 1 0 1
+ light (Light 0) $= Enabled
+ pointSmooth $= Enabled
+
+ colorMaterial $= Just (Front,AmbientAndDiffuse)
+ materialDiffuse Front $= Color4 0.5 0.5 0.5 1
+ materialAmbient Front $= Color4 0.5 0.5 0.5 1
+ materialSpecular Front $= Color4 0.2 0.2 0.2 1
+ materialEmission Front $= Color4 0.3 0.3 0.3 1
+ materialShininess Front $= 90.0
+ -- We enter the main loop
+ mainLoop
+
+-- When no user input entered do nothing
+idle :: (Time -> worldType -> worldType) -> IORef worldType -> IO ()
+idle worldTranformer world = do
+ w <- get world
+ t <- get elapsedTime
+ world $= worldTranformer t w
+ postRedisplay Nothing
+
+-- | Get User Input
+-- | both cleaner, terser and more expendable than the preceeding code
+keyboardMouse :: InputMap a -> IORef a
+ -> Key -> KeyState -> Modifiers -> Position -> IO()
+keyboardMouse input world key state _ _ =
+ when (state == Down) $
+ let
+ charFromKey (Char c) = c
+ -- To complete if you want to finish it
+ charFromKey _ = '#'
+
+ transformator = Map.lookup (Press (charFromKey key)) input
+ in
+ mayTransform transformator
+ where
+ mayTransform Nothing = return ()
+ mayTransform (Just transform) = do
+ w <- get world
+ world $= transform w
+
+
+-- | The function that will display datas
+display :: (HasGetter g, DisplayableWorld world) =>
+ g world -> IO ()
+display worldRef = do
+ -- BEWARE UGLINESS!!!!
+ -- SHOULD NEVER MODIFY worldRef HERE!!!!
+ --
+ -- I SAID NEVER.
+ w <- get worldRef
+ -- NO REALLY, NEVER!!!!
+ -- If someone write a line starting by
+ -- w $= ... Shoot him immediately in the head
+ -- and refere to competent authorities
+ let cam = camera w
+ -- set the background color (dark solarized theme)
+ -- Could also be externalized to world state
+ clearColor $= Color4 0 0.1686 0.2117 1
+ clear [ColorBuffer,DepthBuffer]
+ -- Transformation to change the view
+ loadIdentity -- reset any transformation
+ -- tranlate
+ translate $ toGLVector3 (camPos cam)
+ -- zoom
+ scale (camZoom cam) (camZoom cam) (camZoom cam)
+ -- rotate
+ rotate (xpoint (camDir cam)) $ Vector3 1.0 0.0 (0.0::GLfloat)
+ rotate (ypoint (camDir cam)) $ Vector3 0.0 1.0 (0.0::GLfloat)
+ rotate (zpoint (camDir cam)) $ Vector3 0.0 0.0 (1.0::GLfloat)
+ -- Now that all transformation were made
+ -- We create the object(s)
+ _ <- preservingMatrix $ mapM drawObject (objects w)
+ swapBuffers -- refresh screen
+
+-- Hexa style colors
+scalarFromHex :: String -> Scalar
+scalarFromHex = (/256) . fst . head . readHex
+
+-- | Color from CSS style color string
+hexColor :: String -> Color
+hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru])
+ (scalarFromHex [gd,gu])
+ (scalarFromHex [bd,bu])
+hexColor ('#':r:g:b:[]) = hexColor ['#',r,r,g,g,b,b]
+hexColor _ = error "Bad color!!!!"
+
+-- | Helper to make a color from RGB scalar values
+makeColor :: Scalar -> Scalar -> Scalar -> Color
+makeColor = Color3
+
+-- | Where the drawing occurs
+drawObject :: YObject -> IO()
+drawObject shape = renderPrimitive Triangles $
+ mapM_ drawAtom (atoms shape)
+
+-- simply draw an Atom
+drawAtom :: Atom -> IO ()
+drawAtom atom@(ColoredTriangle (p0,p1,p2,c)) = do
+ color c
+ normal $ toGLNormal3 (getNormal atom)
+ vertex $ toGLVertex3 p0
+ vertex $ toGLVertex3 p1
+ vertex $ toGLVertex3 p2
+
+-- | get the normal vector of an Atom
+-- I don't normalize it; it is done by OpenGL
+-- in main with 'normalize $= Enabled'
+getNormal :: Atom -> Point3D
+getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0)
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/ExtComplex.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/ExtComplex.hs
new file mode 100644
index 0000000..caba8d0
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/ExtComplex.hs
@@ -0,0 +1,37 @@
+module ExtComplex where
+
+import Graphics.Rendering.OpenGL
+
+-- This time I use unpacked strict data type
+-- Far faster when compiled.
+data ExtComplex = C {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ {-# UNPACK #-} !GLfloat
+ deriving (Show,Eq)
+
+instance Num ExtComplex where
+ -- The shape of the 3D mandelbrot
+ -- will depend on this formula
+ (C x y z) * (C x' y' z') = C (x*x' - y*y' - z*z')
+ (x*y' + y*x' + z*z')
+ (x*z' + z*x' )
+ -- The rest is straightforward
+ fromInteger n = C (fromIntegral n) 0 0
+ (C x y z) + (C x' y' z') = C (x+x') (y+y') (z+z')
+ abs (C x y z) = C (sqrt (x*x + y*y + z*z)) 0 0
+ signum (C x y z) = C (signum x) (signum y) (signum z)
+
+extcomplex :: GLfloat -> GLfloat -> GLfloat -> ExtComplex
+extcomplex x y z = C x y z
+
+real :: ExtComplex -> GLfloat
+real (C x _ _) = x
+
+im :: ExtComplex -> GLfloat
+im (C _ y _) = y
+
+strange :: ExtComplex -> GLfloat
+strange (C _ _ z) = z
+
+magnitude :: ExtComplex -> GLfloat
+magnitude = real.abs
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandel.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandel.hs
new file mode 100644
index 0000000..7c1ef34
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandel.hs
@@ -0,0 +1,14 @@
+-- The Mandelbrot function
+module Mandel (mandel) where
+
+import ExtComplex
+
+mandel :: Float -> Float -> Float -> Int -> Int
+mandel r i s nbIterations =
+ f (extcomplex r i s) 0 nbIterations
+ where
+ f :: ExtComplex -> ExtComplex -> Int -> Int
+ f _ _ 0 = 0
+ f c z n = if (magnitude z > 2 )
+ then n
+ else f c ((z*z)+c) (n-1)
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs
new file mode 100644
index 0000000..90b9e04
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/Mandelbulb.lhs
@@ -0,0 +1,220 @@
+ ## Optimization
+
+Our code architecture feel very clean.
+All the meaningful code is in our main file and all display details are
+externalized.
+If you read the code of `YGL.hs`, you'll see I didn't made everything perfect.
+For example, I didn't finished the code of the lights.
+But I believe it is a good first step and it will be easy to go further.
+Unfortunately the program of the preceding session is extremely slow.
+We compute the Mandelbulb for each frame now.
+
+Before our program structure was:
+
+
+Constant Function -> Constant List of Triangles -> Display
+
+
+Now we have
+
+
+Main loop -> World -> Function -> List of Objects -> Atoms -> Display
+
+
+The World state could change.
+The compiler can no more optimize the computation for us.
+We have to manually explain when to redraw the shape.
+
+To optimize we must do some things in a lower level.
+Mostly the program remains the same,
+but it will provide the list of atoms directly.
+
+
+
+> data World = World {
+> angle :: Point3D
+> , anglePerSec :: Scalar
+> , scale :: Scalar
+> , position :: Point3D
+> , box :: Box3D
+> , told :: Time
+> -- We replace shape by cache
+> , cache :: [YObject]
+> }
+
+
+> instance DisplayableWorld World where
+> winTitle _ = "The YGL Mandelbulb"
+> camera w = Camera {
+> camPos = position w,
+> camDir = angle w,
+> camZoom = scale w }
+> -- We update our objects instanciation
+> objects = cache
+
+
+
+> xdir :: Point3D
+> xdir = makePoint3D (1,0,0)
+> ydir :: Point3D
+> ydir = makePoint3D (0,1,0)
+> zdir :: Point3D
+> zdir = makePoint3D (0,0,1)
+>
+> rotate :: Point3D -> Scalar -> World -> World
+> rotate dir angleValue world =
+> world {
+> angle = angle world + (angleValue -*< dir) }
+>
+> switchRotation :: World -> World
+> switchRotation world =
+> world {
+> anglePerSec = if anglePerSec world > 0 then 0 else 5.0 }
+>
+> translate :: Point3D -> Scalar -> World -> World
+> translate dir len world =
+> world {
+> position = position world + (len -*< dir) }
+>
+> zoom :: Scalar -> World -> World
+> zoom z world = world {
+> scale = z * scale world }
+
+> main :: IO ()
+> main = yMainLoop inputActionMap idleAction initialWorld
+
+
+
+Our initial world state is slightly changed:
+
+> -- We initialize the world state
+> -- then angle, position and zoom of the camera
+> -- And the shape function
+> initialWorld :: World
+> initialWorld = World {
+> angle = makePoint3D (30,30,0)
+> , anglePerSec = 5.0
+> , position = makePoint3D (0,0,0)
+> , scale = 1.0
+> , box = Box3D { minPoint = makePoint3D (0-eps, 0-eps, 0-eps)
+> , maxPoint = makePoint3D (0+eps, 0+eps, 0+eps)
+> , resolution = 0.02 }
+> , told = 0
+> -- We declare cache directly this time
+> , cache = objectFunctionFromWorld initialWorld
+> }
+> where eps=2
+
+The use of `eps` is a hint to make a better zoom by computing with the right bounds.
+
+We use the `YGL.getObject3DFromShapeFunction` function directly.
+This way instead of providing `XYFunc`, we provide directly a list of Atoms.
+
+> objectFunctionFromWorld :: World -> [YObject]
+> objectFunctionFromWorld w = [Atoms atomList]
+> where atomListPositive =
+> getObject3DFromShapeFunction
+> (shapeFunc (resolution (box w))) (box w)
+> atomList = atomListPositive ++
+> map negativeTriangle atomListPositive
+> negativeTriangle (ColoredTriangle (p1,p2,p3,c)) =
+> ColoredTriangle (negz p1,negz p3,negz p2,c)
+> where negz (P (x,y,z)) = P (x,y,-z)
+
+We know that resize is the only world change that necessitate to
+recompute the list of atoms (triangles).
+Then we update our world state accordingly.
+
+> resize :: Scalar -> World -> World
+> resize r world =
+> tmpWorld { cache = objectFunctionFromWorld tmpWorld }
+> where
+> tmpWorld = world { box = (box world) {
+> resolution = sqrt ((resolution (box world))**2 * r) }}
+
+All the rest is exactly the same.
+
+
+
+> idleAction :: Time -> World -> World
+> idleAction tnew world =
+> world {
+> angle = angle world + (delta -*< zdir)
+> , told = tnew
+> }
+> where
+> delta = anglePerSec world * elapsed / 1000.0
+> elapsed = fromIntegral (tnew - (told world))
+>
+> shapeFunc :: Scalar -> Function3D
+> shapeFunc res x y =
+> let
+> z = maxZeroIndex (ymandel x y) 0 1 20
+> in
+> if and [ maxZeroIndex (ymandel (x+xeps) (y+yeps)) 0 1 20 < 0.000001 |
+> val <- [res], xeps <- [-val,val], yeps<-[-val,val]]
+> then Nothing
+> else Just (z,colorFromValue 0)
+>
+> colorFromValue :: Point -> Color
+> colorFromValue n =
+> let
+> t :: Point -> Scalar
+> t i = 0.0 + 0.5*cos( i /10 )
+> in
+> makeColor (t n) (t (n+5)) (t (n+10))
+>
+> -- given f min max nbtest,
+> -- considering
+> -- - f is an increasing function
+> -- - f(min)=0
+> -- - f(max)≠0
+> -- then maxZeroIndex f min max nbtest returns x such that
+> -- f(x - ε)=0 and f(x + ε)≠0
+> -- where ε=(max-min)/2^(nbtest+1)
+> maxZeroIndex :: (Fractional a,Num a,Num b,Eq b) =>
+> (a -> b) -> a -> a -> Int -> a
+> maxZeroIndex _ minval maxval 0 = (minval+maxval)/2
+> maxZeroIndex func minval maxval n =
+> if func medpoint /= 0
+> then maxZeroIndex func minval medpoint (n-1)
+> else maxZeroIndex func medpoint maxval (n-1)
+> where medpoint = (minval+maxval)/2
+>
+> ymandel :: Point -> Point -> Point -> Point
+> ymandel x y z = fromIntegral (mandel x y z 64) / 64
+
+
+
+And you can also consider minor changes in the `YGL.hs` source file.
+
+- [`YGL.hs`](code/06_Mandelbulb/YGL.hs), the 3D rendering framework
+- [`Mandel`](code/06_Mandelbulb/Mandel.hs), the mandel function
+- [`ExtComplex`](code/06_Mandelbulb/ExtComplex.hs), the extended complexes
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/YGL.hs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/YGL.hs
new file mode 100644
index 0000000..ee70ae7
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/06_Mandelbulb/YGL.hs
@@ -0,0 +1,385 @@
+{-
+The module YGL will contains most boilerplate
+And display details.
+
+To make things even nicer, we should separate
+this file in many different parts.
+Typically separate the display function.
+
+-}
+module YGL (
+ -- Here is declared our interface with external files
+ -- that will include our YGL module
+
+ -- Declarations related to data types
+ Point -- the 1 dimension point type
+ , Time -- the type for the time
+ , Scalar -- the type for scalar values
+ , Color -- the type for color (3 scalars)
+ , Point3D (..) -- A 3D point type (3 Points)
+ , makePoint3D -- helper (x,y,z) -> Point3D
+ , (-*<) -- scalar product on Point3D a -*< (x,y,z) = (ax,ay,az)
+ , Function3D -- Point -> Point -> Maybe (Point,Color)
+ , xpoint, ypoint, zpoint
+ , Atom (..) -- The Atom object (colored triangles for now)
+
+ -- Your world state must be an instance
+ -- of the DisplayableWorld type class
+ , DisplayableWorld (..)
+ -- Datas related to DisplayableWorld
+ , Camera (..)
+ , YObject (..) -- 3D Objects to display
+ , Box3D (..) -- Some bounded 3D box
+ , getObject3DFromShapeFunction
+ , makeBox -- helper to make a box
+ , hexColor -- Color from hexadecimal string
+ , makeColor -- make color from RGB values
+
+ -- Interface related to user input
+ , InputMap
+ , UserInput (Press,Ctrl,Alt,CtrlAlt)
+ , inputMapFromList
+
+ -- The main loop function to call
+ , yMainLoop
+) where
+
+-- A bunch of imports
+import Numeric (readHex) -- to read hexadecimal values
+
+-- Import of OpenGL and GLUT
+-- but, I use my own Color type, therefore I hide the definition
+-- of Color inside GLUT and OpenGL packages
+import Graphics.Rendering.OpenGL hiding (Color)
+import Graphics.UI.GLUT hiding (Color)
+import Data.IORef
+
+-- I use Map to deal with user interaction
+import qualified Data.Map as Map
+
+-- Some standard stuff
+import Control.Monad (when)
+import Data.Maybe (isNothing)
+
+{-- Things start to be complex here.
+- Just take the time to follow me.
+--}
+
+-- | A 1D point
+type Point = GLfloat
+-- | A Scalar value
+type Scalar = GLfloat
+-- | The time type (currently its Int)
+type Time = Int
+-- | A 3D Point mainly '(x,y,z)'
+data Point3D = P (Point,Point,Point) deriving (Eq,Show,Read)
+type Color = Color3 Scalar
+
+-- Get x (resp. y, z) coordinate of a 3D point
+xpoint :: Point3D -> Point
+xpoint (P (x,_,_)) = x
+ypoint :: Point3D -> Point
+ypoint (P (_,y,_)) = y
+zpoint :: Point3D -> Point
+zpoint (P (_,_,z)) = z
+
+-- Create a Point3D element from a triplet
+makePoint3D :: (Point,Point,Point) -> Point3D
+makePoint3D = P
+
+-- Make Point3D an instance of Num
+instance Num Point3D where
+ (+) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax+bx,ay+by,az+bz)
+ (-) (P (ax,ay,az)) (P (bx,by,bz)) = P (ax-bx,ay-by,az-bz)
+ (*) (P (ax,ay,az)) (P (bx,by,bz)) = P ( ay*bz - az*by
+ , az*bx - ax*bz
+ , ax*by - ay*bx )
+ abs (P (x,y,z)) = P (abs x,abs y, abs z)
+ signum (P (x,y,z)) = P (signum x, signum y, signum z)
+ fromInteger i = P (fromInteger i, 0, 0)
+
+-- The scalar product
+infixr 5 -*<
+(-*<) :: Scalar -> Point3D -> Point3D
+(-*<) s p = P (s*xpoint p, s*ypoint p, s*zpoint p)
+
+-- Used internally to convert point3D to different types
+toGLVector3 :: Point3D -> Vector3 GLfloat
+toGLVector3 (P(x,y,z)) = Vector3 x y z
+
+toGLVertex3 :: Point3D -> Vertex3 GLfloat
+toGLVertex3 (P(x,y,z)) = Vertex3 x y z
+
+toGLNormal3 :: Point3D -> Normal3 GLfloat
+toGLNormal3 (P(x,y,z)) = Normal3 x y z
+
+-- | The Box3D type represent a 3D bounding box
+-- | Note if minPoint = (x,y,z) and maxPoint = (x',y',z')
+-- | Then to have a non empty box you must have
+-- | x (Point,Point,Point) -> Scalar -> Box3D
+makeBox mini maxi res = Box3D {
+ minPoint = makePoint3D mini
+ , maxPoint = makePoint3D maxi
+ , resolution = res }
+
+-- | A Triangle3D is simply 3 points and a color
+type Triangle3D = (Point3D,Point3D,Point3D,Color)
+
+-- | The type Atom is the atom for our display here we'll only use triangles.
+-- | For a general purpose library we should add many other different atoms
+-- | corresponding to Quads for example.
+data Atom = ColoredTriangle Triangle3D
+
+-- | A Function3D is simply a function for each x,y associate a z and a color
+-- | If undefined at point (x,y), it returns Nothing.
+type Function3D = Point -> Point -> Maybe (Point,Color)
+
+-- | Our objects that will be displayed
+-- | Wether a function3D delimited by a Box
+-- | or a list of Atoms
+data YObject = XYFunc Function3D Box3D
+ | Atoms [Atom]
+
+-- | The function atoms retrieve the list of atoms from an YObject
+atoms :: YObject -> [Atom]
+atoms (XYFunc f b) = getObject3DFromShapeFunction f b
+atoms (Atoms atomList) = atomList
+
+-- | We decalre the input map type we need here
+-- | It is our API
+-- | I don't use Mouse but it can be easily added
+type InputMap worldType = Map.Map UserInput (worldType -> worldType)
+data UserInput = Press Char | Ctrl Char | Alt Char | CtrlAlt Char
+ deriving (Eq,Ord,Show,Read)
+
+-- | A displayable world is a type for which
+-- | ther exists a function that provide sufficient informations
+-- | to provide a camera, lights, objects and a window title.
+class DisplayableWorld world where
+ camera :: world -> Camera
+ camera _ = defaultCamera
+ lights :: world -> [Light]
+ lights _ = []
+ objects :: world -> [YObject]
+ objects _ = []
+ winTitle :: world -> String
+ winTitle _ = "YGL"
+
+-- | the Camera type to know how to
+-- | Transform the scene to see the right view.
+data Camera = Camera {
+ camPos :: Point3D
+ , camDir :: Point3D
+ , camZoom :: Scalar }
+
+-- | A default initial camera
+defaultCamera :: Camera
+defaultCamera = Camera {
+ camPos = makePoint3D (0,0,0)
+ , camDir = makePoint3D (0,0,0)
+ , camZoom = 1 }
+
+
+-- | Given a shape function and a delimited Box3D
+-- | return a list of Atoms (here only colored triangles) to be displayed
+getObject3DFromShapeFunction :: Function3D -> Box3D -> [Atom]
+getObject3DFromShapeFunction shape box = do
+ x <- [xmin,xmin+res..xmax]
+ y <- [ymin,ymin+res..ymax]
+ let
+ neighbors = [(x,y),(x+res,y),(x+res,y+res),(x,y+res)]
+ -- zs are 3D points with found depth and color
+ -- zs :: [ (Point,Point,Point,Maybe (Point,Color) ]
+ zs = map (\(u,v) -> (u,v,shape u v)) neighbors
+ -- ps are 3D opengl points + color value
+ ps = zs
+ -- If the point diverged too fast, don't display it
+ if any (\(_,_,z) -> isNothing z) zs
+ then []
+ -- Draw two triangles
+ -- 3 - 2
+ -- | / |
+ -- 0 - 1
+ -- The order is important
+ else
+ [ makeAtom (ps!!0) (ps!!2) (ps!!1)
+ , makeAtom (ps!!0) (ps!!3) (ps!!2) ]
+ where
+ makeAtom (p0x,p0y,Just (p0z,c0)) (p1x,p1y,Just (p1z,_)) (p2x,p2y,Just (p2z,_)) =
+ ColoredTriangle (makePoint3D (p0x,p0y,p0z)
+ ,makePoint3D (p1x,p1y,p1z)
+ ,makePoint3D (p2x,p2y,p2z)
+ ,c0)
+ makeAtom _ _ _ = error "Somethings wrong here"
+
+ -- some naming to make it
+ -- easier to read
+ xmin = xpoint $ minPoint box
+ xmax = xpoint $ maxPoint box
+ ymin = ypoint $ minPoint box
+ ymax = ypoint $ maxPoint box
+ res = resolution box
+
+-- | Get the user input map from a list
+inputMapFromList :: (DisplayableWorld world) =>
+ [(UserInput,world -> world)] -> InputMap world
+inputMapFromList = Map.fromList
+
+{--
+- We set our mainLoop function
+- As you can see the code is _not_ pure
+- and not even functionnal friendly!
+- But when called,
+- it will look like a pure functional function.
+--}
+yMainLoop :: (DisplayableWorld worldType) =>
+ -- the mapping user input / world
+ InputMap worldType
+ -- function that modify the world
+ -> (Time -> worldType -> worldType)
+ -- the world state of type worldType
+ -> worldType
+ -- into IO () for obvious reason
+ -> IO ()
+yMainLoop inputActionMap
+ worldTranformer
+ world = do
+ -- The boilerplate
+ _ <- getArgsAndInitialize
+ initialDisplayMode $=
+ [WithDepthBuffer,DoubleBuffered,RGBMode]
+ _ <- createWindow $ winTitle world
+ depthFunc $= Just Less
+ windowSize $= Size 500 500
+ -- The state variables for the world (I know it feels BAD)
+ worldRef <- newIORef world
+ -- Action to call when waiting
+ idleCallback $= Just (idle worldTranformer worldRef)
+ -- the keyboard will update the world
+ keyboardMouseCallback $=
+ Just (keyboardMouse inputActionMap worldRef)
+ -- We generate one frame using the callback
+ displayCallback $= display worldRef
+ -- let OpenGL resize normal vectors to unity
+ normalize $= Enabled
+ shadeModel $= Smooth
+ -- Lights (in a better version should be put elsewhere)
+ lighting $= Enabled
+ ambient (Light 0) $= Color4 0.5 0.5 0.5 1
+ diffuse (Light 0) $= Color4 1 1 1 1
+ light (Light 0) $= Enabled
+ pointSmooth $= Enabled
+
+ colorMaterial $= Just (Front,AmbientAndDiffuse)
+ materialAmbient Front $= Color4 0.0 0.0 0.0 1
+ materialDiffuse Front $= Color4 0.0 0.0 0.0 1
+ materialSpecular Front $= Color4 1 1 1 1
+ materialEmission Front $= Color4 0.0 0.0 0.0 1
+ materialShininess Front $= 96
+ -- We enter the main loop
+ mainLoop
+
+-- When no user input entered do nothing
+idle :: (Time -> worldType -> worldType) -> IORef worldType -> IO ()
+idle worldTranformer world = do
+ w <- get world
+ t <- get elapsedTime
+ world $= worldTranformer t w
+ postRedisplay Nothing
+
+-- | Get User Input
+-- | both cleaner, terser and more expendable than the preceeding code
+keyboardMouse :: InputMap a -> IORef a
+ -> Key -> KeyState -> Modifiers -> Position -> IO()
+keyboardMouse input world key state _ _ =
+ when (state == Down) $
+ let
+ charFromKey (Char c) = c
+ -- To complete if you want to finish it
+ charFromKey _ = '#'
+
+ transformator = Map.lookup (Press (charFromKey key)) input
+ in
+ mayTransform transformator
+ where
+ mayTransform Nothing = return ()
+ mayTransform (Just transform) = do
+ w <- get world
+ world $= transform w
+
+
+-- | The function that will display datas
+display :: (HasGetter g, DisplayableWorld world) =>
+ g world -> IO ()
+display worldRef = do
+ -- BEWARE UGLINESS!!!!
+ -- SHOULD NEVER MODIFY worldRef HERE!!!!
+ --
+ -- I SAID NEVER.
+ w <- get worldRef
+ -- NO REALLY, NEVER!!!!
+ -- If someone write a line starting by
+ -- w $= ... Shoot him immediately in the head
+ -- and refere to competent authorities
+ let cam = camera w
+ -- set the background color (dark solarized theme)
+ -- Could also be externalized to world state
+ clearColor $= Color4 0 0.1686 0.2117 1
+ clear [ColorBuffer,DepthBuffer]
+ -- Transformation to change the view
+ loadIdentity -- reset any transformation
+ -- tranlate
+ translate $ toGLVector3 (camPos cam)
+ -- zoom
+ scale (camZoom cam) (camZoom cam) (camZoom cam)
+ -- rotate
+ rotate (xpoint (camDir cam)) $ Vector3 1.0 0.0 (0.0::GLfloat)
+ rotate (ypoint (camDir cam)) $ Vector3 0.0 1.0 (0.0::GLfloat)
+ rotate (zpoint (camDir cam)) $ Vector3 0.0 0.0 (1.0::GLfloat)
+ -- Now that all transformation were made
+ -- We create the object(s)
+ _ <- preservingMatrix $ mapM drawObject (objects w)
+ swapBuffers -- refresh screen
+
+-- Hexa style colors
+scalarFromHex :: String -> Scalar
+scalarFromHex = (/256) . fst . head . readHex
+
+-- | Color from CSS style color string
+hexColor :: String -> Color
+hexColor ('#':rd:ru:gd:gu:bd:bu:[]) = Color3 (scalarFromHex [rd,ru])
+ (scalarFromHex [gd,gu])
+ (scalarFromHex [bd,bu])
+hexColor ('#':r:g:b:[]) = hexColor ['#',r,r,g,g,b,b]
+hexColor _ = error "Bad color!!!!"
+
+-- | Helper to make a color from RGB scalar values
+makeColor :: Scalar -> Scalar -> Scalar -> Color
+makeColor = Color3
+
+-- | Where the drawing occurs
+drawObject :: YObject -> IO()
+drawObject shape = renderPrimitive Triangles $
+ mapM_ drawAtom (atoms shape)
+
+-- simply draw an Atom
+drawAtom :: Atom -> IO ()
+drawAtom atom@(ColoredTriangle (p0,p1,p2,c)) = do
+ color c
+ normal $ toGLNormal3 (getNormal atom)
+ vertex $ toGLVertex3 p0
+ vertex $ toGLVertex3 p1
+ vertex $ toGLVertex3 p2
+
+-- | get the normal vector of an Atom
+-- I don't normalize it; it is done by OpenGL
+-- in main with 'normalize $= Enabled'
+getNormal :: Atom -> Point3D
+getNormal (ColoredTriangle (p0,p1,p2,_)) = (p1 - p0) * (p2 - p0)
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs
new file mode 100644
index 0000000..d222d3f
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/code/07_Conclusion/Conclusion.lhs
@@ -0,0 +1,25 @@
+ ## Conclusion
+
+As we can use imperative style in a functional language,
+know you can use functional style in imperative languages.
+This article exposed a way to organize some code in a functional way.
+I'd like to stress the usage of Haskell made it very simple to achieve this.
+
+Once you are used to pure functional style,
+it is hard not to see all advantages it offers.
+
+The code in the two last sections is completely pure and functional.
+Furthermore I don't use `GLfloat`, `Color3` or any other OpenGL type.
+If I want to use another library in the future,
+I would be able to keep all the pure code and simply update the YGL module.
+
+The `YGL` module can be seen as a "wrapper" around 3D display and user interaction.
+It is a clean separator between the imperative paradigm and functional paradigm.
+
+If you want to go further, it shouldn't be hard to add parallelism.
+This should be easy mainly because most of the visible code is pure.
+Such an optimization would have been harder by using directly the OpenGL library.
+
+You should also want to make a more precise object. Because, the Mandelbulb is
+clearly not convex. But a precise rendering might be very long from
+O(n².log(n)) to O(n³).
diff --git a/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/index.html b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/index.html
new file mode 100644
index 0000000..72dc199
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-OpenGL-Mandelbrot/index.html
@@ -0,0 +1,1240 @@
+
+
+
+
+ YBlog - Haskell Progressive Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
tl;dr: A progressive Haskell example. A Mandelbrot set extended in 3D, rendered using OpenGL and coded with Haskell. In the end the code will be very clean. The significant stuff will be in a pure functional bubble. The display details will be put in an external module playing the role of a wrapper. Imperative language could also benefit from this functional organization.
This article goes further. It will show how to use functional programming with interactive programs. But more than that, it will show how to organize your code in a functional way. This article is more about functional paradigm than functional language. The code organization can be used in most imperative language.
+
As Haskell is designed for functional paradigm, it is easier to use in this context. In reality, the firsts sections will use an imperative paradigm. As you can use functional paradigm in imperative language, you can also use imperative paradigm in functional languages.
+
This article is about creating an useful and clean program. It can interact with the user in real time. It uses OpenGL, a library with imperative programming foundations. Despite this fact, most of the final code will remain in the pure part (no IO).
+
I believe the main audience for this article are:
+
+
Haskell programmer looking for an OpengGL tutorial.
+
People interested in program organization (programming language agnostic).
+
Fractal lovers and in particular 3D fractal.
+
People interested in user interaction in a functional paradigm.
+
+
I had in mind for some time now to make a Mandelbrot set explorer. I had already written a command line Mandelbrot set generator in Haskell. This utility is highly parallel; it uses the repa package1.
+
This time, we will not parallelize the computation. Instead, we will display the Mandelbrot set extended in 3D using OpenGL and Haskell. You will be able to move it using your keyboard. This object is a Mandelbrot set in the plan (z=0), and something nice to see in 3D.
+
Here are some screenshots of the result:
+
+
+
+
And you can see the intermediate steps to reach this goal:
+
+
+
+
From the 2nd section to the 4th it will be dirtier and dirtier. We start cleaning the code at the 5th section.
Also here, there is only one interesting line; the draw will occur in the function drawMandelbrot.
+
This function will provide a list of draw actions. Remember that OpenGL is imperative by design. Then, one of the consequence is you must write the actions in the right order. No easy parallel drawing here. Here is the function which will render something on the screen:
The mapM_ function is mainly the same as map but inside a monadic context. More precisely, this can be transformed as a list of actions where the order is important:
+
drawMandelbrot =
+ renderPrimitive Points $ do
+ color color1
+ vertex $ Vertex3 x1 y1 0
+ ...
+ color colorN
+ vertex $ Vertex3 xN yN 0
+
We also need some kind of global variables. In fact, global variable are a proof of a design problem. We will get rid of them later.
It uses the main Mandelbrot function for each complex \(c\). The Mandelbrot set is the set of complex number \(c\) such that the following sequence does not escape to infinity.
+
Let us define \(f_c: \)
+
fc(z) = z2 + c
+
The sequence is:
+
0 → fc(0) → fc(fc(0)) → ⋯ → fcn(0) → ⋯
+
Of course, instead of trying to test the real limit, we just make a test after a finite number of occurrences.
Well, if you download this file (look at the bottom of this section), compile it and run it this is the result:
+
+
+
+
A first very interesting property of this program is that the computation for all the points is done only once. It is a bit long before the first image appears, but if you resize the window, it updates instantaneously. This property is a direct consequence of purity. If you look closely, you see that allPoints is a pure list. Therefore, calling allPoints will always render the same result and Haskell is clever enough to use this property. While Haskell doesn’t garbage collect allPoints the result is reused for free. We did not specified this value should be saved for later use. It is saved for us.
+
See what occurs if we make the window bigger:
+
+
+
+
We see some black lines because we have drawn less point than there is on the surface. We can repair this by drawing little squares instead of just points. But, instead we will do something a bit different and unusual.
This time, instead of drawing all points, we will simply draw the edges of the Mandelbrot set. The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough for the purpose of this tutorial.
+
We change slightly the drawMandelbrot function. We replace the Points by LineLoop
This function is interesting. For those not used to the list monad here is a natural language version of this function:
+
positivePoints =
+ for all x in the range [-width..width]
+ let y be smallest number s.t. mandel x y > 0
+ if y is on 0 then don't return a point
+ else return the value corresonding to (x,y,color for (x+iy))
+
In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that mandel x y > 0 we use a simple dichotomy:
Now we will we extend to a third dimension. But, there is no 3D equivalent to complex. In fact, the only extension known are quaternions (in 4D). As I know almost nothing about quaternions, I will use some extended complex, instead of using a 3D projection of quaternions. I am pretty sure this construction is not useful for numbers. But it will be enough for us to create something that look nice.
+
This section is quite long, but don’t be afraid, most of the code is some OpenGL boilerplate. If you just want to skim this section, here is a high level representation:
+
+
+
OpenGL Boilerplate
+
+
set some IORef (understand variables) for states
+
Drawing:
+
+
set doubleBuffer, handle depth, window size…
+
Use state to apply some transformations
+
+
Keyboard: hitting some key change the state of IORef
The most important part is the new multiplication instance. Modifying this formula will change radically the shape of the result. Here is the formula written in a more mathematical notation. I called the third component of these extended complex strange.
+
real((x, y, z) × (x′, y′, z′)) = xx′ − yy′ − zz′
+
im((x, y, z) × (x′, y′, z′)) = xy′ − yx′ + zz′
+
strange((x, y, z) × (x′, y′, z′)) = xz′ + zx′
+
Note how if \(z=z’=0\) then the multiplication is the same to the complex one.
As we will use some 3D, we add some new directive in the boilerplate. But mainly, we simply state that will use some depth buffer. And also we will listen the keyboard.
We introduce some helper function to manipulate standard IORef. Mainly modVar x f is equivalent to the imperative x:=f(x), modFst (x,y) (+1) is equivalent to (x,y) := (x+1,y) and modSnd (x,y) (+1) is equivalent to (x,y) := (x,y+1)
And we use them to code the function handling keyboard. We will use the keys hjkl to rotate, oi to zoom and sedf to move. Also, hitting space will reset the view. Remember that angle and campos are pairs and zoom is a scalar. Also note (+0.5) is the function \x->x+0.5 and (-0.5) is the number -0.5 (yes I share your pain).
Not much to say about this function. Mainly there are two parts: apply some transformations, draw the object.
+
The 3D Mandelbrot
+
We have finished with the OpenGL section, let’s talk about how we generate the 3D points and colors. First, we will set the number of details to 200 pixels in the three dimensions.
This time, instead of just drawing some line or some group of points, we will show triangles. The function allPoints will provide a multiple of three points. Each three successive point representing the coordinate of each vertex of a triangle.
In fact, we will provide six ordered points. These points will be used to draw two triangles.
+
+
+
+
The next function is a bit long. Here is an approximative English version:
+
forall x from -width to width
+ forall y from -height to height
+ forall the neighbors of (x,y)
+ let z be the smalled depth such that (mandel x y z)>0
+ let c be the color given by mandel x y z
+ add the point corresponding to (x,y,z,c)
+
Also, I added a test to hide points too far from the border. In fact, this function show points close to the surface of the modified mandelbrot set. But not the mandelbrot set itself.
If you look at the function above, you see a lot of common patterns. Haskell is very efficient to make this better. Here is a harder to read but shorter and more generic rewritten function:
If you prefer the first version, then just imagine how hard it will be to change the enumeration of the point from (x,y) to (x,z) for example.
+
Also, we didn’t searched for negative values. This modified Mandelbrot is no more symmetric relatively to the plan y=0. But it is symmetric relatively to the plan z=0. Then I mirror these values.
The first approach to clean the code is to separate the GLUT/OpenGL part from the computation of the shape. Here is the cleaned version of the preceding section. Most boilerplate was put in external files.
This code is cleaner but many things doesn’t feel right. First, all the user interaction code is outside our main file. I feel it is okay to hide the detail for the rendering. But I would have preferred to control the user actions.
+
On the other hand, we continue to handle a lot rendering details. For example, we provide ordered vertices.
OpenGL and GLUT is done in C. In particular the mainLoop function is a direct link to the C library (FFI). This function is clearly far from the functional paradigm. Could we make this better? We will have two choices:
+
+
create our own mainLoop function to make it more functional.
+
deal with the imperative nature of the GLUT mainLoop function.
+
+As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the mainLoop function.
+
Our main problem come from user interaction. If you ask “the Internet”, about how to deal with user interaction with a functional paradigm, the main answer is to use functional reactive programming (FRP). I won’t use FRP in this article. Instead, I’ll use a simpler while less effective way to deal with user interaction. But The method I’ll use will be as pure and functional as possible.
+
+
Here is how I imagine things should go. First, what the main loop should look like if we could make our own:
+
functionalMainLoop =
+ Read user inputs and provide a list of actions
+ Apply all actions to the World
+ Display one frame
+ repetere aeternum
+
Clearly, ideally we should provide only three parameters to this main loop function:
+
+
an initial World state
+
a mapping between the user interactions and functions which modify the world
+
a function taking two parameters: time and world state and render a new world without user interaction.
+
+
Here is a real working code, I’ve hidden most display functions. The YGL, is a kind of framework to display 3D functions. But it can easily be extended to many kind of representation.
We first set the mapping between user input and actions. The type of each couple should be of the form (user input, f) where (in a first time) f:World -> World. It means, the user input will transform the world state.
The important part to glue our own type to the framework is to make our type an instance of the type class DisplayableWorld. We simply have to provide the definition of some functions.
The camera function will retrieve an object of type Camera which contains most necessary information to set our camera. The objects function will returns a list of objects. Their type is YObject. Note the generation of triangles is no more in this file. Until here we only used declarative pattern.
+
We also need to set all our transformation functions. These function are used to update the world state.
The resize is used to generate the 3D function. As I wanted the time spent to generate a more detailed view to grow linearly I use this not so straightforward formula.
Now the function which will generate points in 3D. The first parameter (res) is the resolution of the vertex generation. More precisely, res is distance between two points on one direction. We need it to “close” our shape.
+
The type Function3D is Point -> Point -> Maybe Point. Because we consider partial functions (for some (x,y) our function can be undefined).
Our code architecture feel very clean. All the meaningful code is in our main file and all display details are externalized. If you read the code of YGL.hs, you’ll see I didn’t made everything perfect. For example, I didn’t finished the code of the lights. But I believe it is a good first step and it will be easy to go further. Unfortunately the program of the preceding session is extremely slow. We compute the Mandelbulb for each frame now.
+
Before our program structure was:
+
Constant Function -> Constant List of Triangles -> Display
+
Now we have
+
Main loop -> World -> Function -> List of Objects -> Atoms -> Display
+
The World state could change. The compiler can no more optimize the computation for us. We have to manually explain when to redraw the shape.
+
To optimize we must do some things in a lower level. Mostly the program remains the same, but it will provide the list of atoms directly.
As we can use imperative style in a functional language, know you can use functional style in imperative languages. This article exposed a way to organize some code in a functional way. I’d like to stress the usage of Haskell made it very simple to achieve this.
+
Once you are used to pure functional style, it is hard not to see all advantages it offers.
+
The code in the two last sections is completely pure and functional. Furthermore I don’t use GLfloat, Color3 or any other OpenGL type. If I want to use another library in the future, I would be able to keep all the pure code and simply update the YGL module.
+
The YGL module can be seen as a “wrapper” around 3D display and user interaction. It is a clean separator between the imperative paradigm and functional paradigm.
+
If you want to go further, it shouldn’t be hard to add parallelism. This should be easy mainly because most of the visible code is pure. Such an optimization would have been harder by using directly the OpenGL library.
+
You should also want to make a more precise object. Because, the Mandelbulb is clearly not convex. But a precise rendering might be very long from O(n².log(n)) to O(n³).
+
+
+
+
Unfortunately, I couldn’t make this program to work on my Mac. More precisely, I couldn’t make the DevIL library work on Mac to output the image. Yes I have done a brew install libdevil. But even a minimal program who simply write some jpg didn’t worked. I tried both with Haskell and C.↩
+
Generally in Haskell you need to declare a lot of import lines. This is something I find annoying. In particular, it should be possible to create a special file, Import.hs which make all the necessary import for you, as you generally need them all. I understand why this is cleaner to force the programmer not to do so, but, each time I do a copy/paste, I feel something is wrong. I believe this concern can be generalized to the lack of namespace in Haskell.↩
+
I tried Complex Double, Complex Float, this current data type with Double and the actual version Float. For rendering a 1024x1024 Mandelbrot set it takes Complex Double about 6.8s, for Complex Float about 5.1s, for the actual version with Double and Float it takes about 1.6 sec. See these sources for testing yourself: https://gist.github.com/2945043. If you really want to things to go faster, use data Complex = C {-# UNPACK #-} !Float {-# UNPACK #-} !Float. It takes only one second instead of 1.6s.↩
tl;dr: Some hints on how to make great documentation for Haskell libraries.
+
+
Create a Tutorial module containing nothing except documentation.
+
Mention the Tutorial module in your cabal description
+
Use doctest to check your documentation is up to date
+
For more complex real world examples, link to the source of some test.
+
+
+
Great documentation make a big difference. A bad documentation could simply make people not using your lib.
+
My friend was learning Haskell. To start he tried a Haskell library to make a small application. The documentation was deprecated to the point he wasn’t able to make a basic example work. How do you believe he felt? What does he thought about Haskell in general?
+
So here are my hint on how to make a great documentation in Haskell.
+
Documentation can take many different form.
+
+
Tutorials/Guides – write some prose which friendly take a user by hand and help him
+
Examples – how to use each function
+
Generated API Documentation – haddock
+
+
Hints
+
Tutorials/Guides
+
+
Create a new module named Tutorial (or Guide.GuideTopic)
+
Create a link to the tutorial in the cabal description
To prevent obsolescence of your tutorial, use doctest.
+
That way when you’ll do a stack test or cabal test you’ll get errors if some example doesn’t work anymore.
+
Examples (doctest)
+
doctest is a great way to provide examples in your code documentation. These example will then be used as tests. Apparently it comes from Python community.
There are plenty of alternative solution. I provide the one I believe would be used by most people. So if you use github simply create an account on travis.
+
Add a .travis.yml file in your repo containing the content of the file here and remove the builds you don’t need. It will build your project using a lot of different GHC versions and environemnts.
+
If you are afraid by such its complexity you might just want to use this one:
If you didn’t declared your package to stackage, please do it. It isn’t much work. Just edit a file to add your package. And you’ll could be able to add another badge:
data MyData a b
+ = C1 a b -- ^ doc for constructor C1
+ | C2 a b -- ^ doc for constructor C2
+
+data MyData a b
+ = C { a :: TypeA -- ^ field a description
+ , b :: TypeB -- ^ field b description
+ }
+
Module:
+
{-|
+Module : MyModule
+Description: Short description
+Copyright : (c)
+License : MIT
+
+Here is a longer description of this module.
+With some code symbol @MyType@.
+And also a block of code:
+
+@
+data MyData = C Int Int
+
+myFunction :: MyData -> Int
+@
+
+-}
+
Documentation Structure:
+
module MyModule (
+ -- * Classes
+ C(..),
+ -- * Types
+ -- ** A data type
+ T,
+ -- ** A record
+ R,
+ -- * Some functions
+ f, g
+ ) where
+
That will generate headings.
+
Other Random Ideas
+
In Haskell we have great tools like hayoo! and hoogle.
+
And hackage and stackage provide also a lot of informations.
+
But generally we lack a lot of Tutorials and Guides. This post was an attempt to help people making more of them.
+
But there are other good ideas to help improve the situation.
+
Create a doc with link to best practices
+
In clojure when you create a new project using lein new my-project a directory doc is created for you. It contains a file with a link to this blog post:
If you try to search for some clojure function on a search engine there is a big chance the first result will link to:
+
+
clojuredocs.org: try to search for reduce, update-in or index for example
+
+
For each symbol necessiting a documentation. You don’t only have the details and standard documentation. You’ll also get:
+
+
Responsive Design (sometime you want to look at documentation on a mobile)
+
Contributed Examples
+
Contributed See Also section
+
Contributed notes/comments
+
+
clojuredocs.org is an independant website from the official Clojure website.
+
Most of the time, if you google the function you search you end up on clojuredocs for wich there are many contributions.
+
Currently stackage is closer to these feature than hackage. Because on stackage you have access to the README and also some comments by package.
+
I believe it would be more efficient to have at least a page by module and why not a page by symbol (data, functions, typeclasses…).
+
For example, we could provide details about foldl for example. Also as there would be less information to display, it will make the design cleaner.
+
Today, if you want to help documenting, you need to make a PR to the source of some library. While if we had an equivalent to clojuredocs for Haskell, adding documentation would simply be a few clicks away:
+
+
login
+
add/edit some example, comments, see-also section
+
+
There are more than 23k people on /r/haskell. If only 1% of them would take 10 minutes adding a bit of documentation it will certainly change a lot of things in the percieved documentation quality.
+
And last but not least,
+
Design is important
+
+
+
+
Design is a vague word. A good design should care not only about how something look, but also how users will interact with it. For example by removing things to focus on the essential.
+
When I stumble upon some random blog post or random specification in the Haskell community, I had too much a feeling of old fashioned design.
+
If you look at node.js community lot of their web page look cleaner, easier to read and in the end, more user friendly.
+
Haskell is very different from node, I wouldn’t like to replace all long and precise documentation with short human unprecise concepts. I don’t want to transform scientific papers by tweets.
+
But like the scientific community has upgraded with the use of LaTeX, I believe we could find something similar that would make, very clean environment for most of us. A kind of look and feel that will be
+
+
modern
+
device friendly (either on computer, mobile, tablet)
+
efficient, focus on what is most important and is helpful
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/00_preamble.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/00_preamble.lhs
new file mode 100644
index 0000000..ae51c26
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/00_preamble.lhs
@@ -0,0 +1,124 @@
+begindiv(intro)
+
+en: I really believe all developers should learn Haskell.
+en: I don't think everyone needs to be super Haskell ninjas,
+en: but they should at least discover what Haskell has to offer.
+en: Learning Haskell opens your mind.
+fr: Je pense vraiment que
+fr: tous les développeurs devraient apprendre Haskell.
+fr: Peut-être pas devenir des ninjas d'Haskell,
+fr: mais au moins savoir ce que ce langage a de particulier.
+fr: Son apprentissage ouvre énormément l'esprit.
+
+en: Mainstream languages share the same foundations:
+fr: La plupart des langages partagent les mêmes fondements :
+
+en: - variables
+en: - loops
+en: - pointers[^0001]
+en: - data structures, objects and classes (for most)
+fr: - les variables
+fr: - les boucles
+fr: - les pointeurs[^0001]
+fr: - les structures de données, les objets et les classes
+
+en: [^0001]: Even if most recent languages try to hide them, they are present.
+fr: [^0001]: Même si tous les langages récents essayent de les cacher, ils restent présents.
+
+en: Haskell is very different.
+en: The language uses a lot of concepts I had never heard about before.
+en: Many of those concepts will help you become a better programmer.
+fr: Haskell est très différent.
+fr: Ce langage utilise des concepts dont je n'avais jamais entendu parler avant.
+fr: Beaucoup de ces concepts pourront vous aider à devenir un meilleur développeur.
+
+en: But learning Haskell can be hard.
+en: It was for me.
+en: In this article I try to provide what I lacked during my learning.
+fr: Plier son esprit à Haskell peut être difficile.
+fr: Ce le fut pour moi.
+fr: Dans cet article, j'essaye de fournir les informations qui m'ont manquées lors de mon apprentissage.
+
+en: This article will certainly be hard to follow.
+en: This is on purpose.
+en: There is no shortcut to learning Haskell.
+en: It is hard and challenging.
+en: But I believe this is a good thing.
+en: It is because it is hard that Haskell is interesting.
+fr: Cet article sera certainement difficile à suivre.
+fr: Mais c'est voulu.
+fr: Il n'y a pas de raccourci pour apprendre Haskell.
+fr: C'est difficile.
+fr: Mais je pense que c'est une bonne chose.
+fr: C'est entre autres parce qu'Haskell est difficile qu'il est intéressant.
+
+en: The conventional method to learning Haskell is to read two books.
+en: First ["Learn You a Haskell"](http://learnyouahaskell.com) and just after ["Real World Haskell"](http://www.realworldhaskell.org).
+en: I also believe this is the right way to go.
+en: But to learn what Haskell is all about, you'll have to read them in detail.
+fr: La manière conventionnelle d'apprendre Haskell est de lire deux livres.
+fr: D'abord ["Learn You a Haskell"](http://haskell.fr/lyah/)
+fr: et ensuite ["Real World Haskell"](http://www.realworldhaskell.org).
+fr: Je pense aussi que c'est la bonne manière de s'y prendre.
+fr: Mais apprendre même un tout petit peu d'Haskell est presque impossible sans se plonger réellement dans ces livres.
+
+en: In contrast, this article is a very brief and dense overview of all major aspects of Haskell.
+en: I also added some information I lacked while I learned Haskell.
+fr: Cet article fait un résumé très dense et rapide des aspects majeurs d'Haskell.
+fr: J'y ai aussi rajouté des informations qui m'ont manqué pendant l'apprentissage de ce langage.
+
+fr: Pour les francophones : je suis désolé.
+fr: Je n'ai pas eu le courage de tout retraduire en français.
+fr: Sachez cependant que si vous êtes plusieurs à insister, je ferai certainement l'effort de traduire l'article en entier.
+fr: Et si vous vous sentez d'avoir une bonne âme je ne suis pas contre un peu d'aide.
+fr: Les sources de cet article sont sur [github](http://github.com/yogsototh/learn_haskell.git).
+
+en: The article contains five parts:
+fr: Cet article contient cinq parties :
+
+en: - Introduction: a short example to show Haskell can be friendly.
+en: - Basic Haskell: Haskell syntax, and some essential notions.
+en: - Hard Difficulty Part:
+en: - Functional style; a progressive example, from imperative to functional style
+en: - Types; types and a standard binary tree example
+en: - Infinite Structure; manipulate an infinite binary tree!
+en: - Hell Difficulty Part:
+en: - Deal with IO; A very minimal example
+en: - IO trick explained; the hidden detail I lacked to understand IO
+en: - Monads; incredible how we can generalize
+en: - Appendix:
+en: - More on infinite tree; a more math oriented discussion about infinite trees
+
+fr: - Introduction : un exemple rapide pour montrer qu'Haskell peut être facile.
+fr: - Les bases d'Haskell : La syntaxe et des notions essentielles
+fr: - Partie difficile :
+fr: - Style fonctionnel : un exemple progressif, du style impératif au style fonctionnel ;
+fr: - Types : la syntaxe et un exemple d'arbre binaire ;
+fr: - Structure infinie : manipulons un arbre infini !
+fr: - Partie de difficulté infernale :
+fr: - Utiliser les IO : un exemple très minimal ;
+fr: - Le truc des IO révélé : les détails cachés d'IO qui m'ont manqués
+fr: - Les monades : incroyable à quel point on peut généraliser
+fr: - Appendice :
+fr: - Revenons sur les arbres infinis : une discussion plus mathématique sur la manipulation d'arbres infinis.
+
+en: > Note: Each time you see a separator with a filename ending in `.lhs`
+en: > you can click the filename to get this file.
+en: > If you save the file as `filename.lhs`, you can run it with
+en: >
+en: > runhaskell filename.lhs
+en: >
+en: >
+en: > Some might not work, but most will.
+en: > You should see a link just below.
+
+fr: > Note: Chaque fois que vous voyez un séparateur avec un nom de fichier se terminant par `lhs`, vous pouvez cliquer sur le nom de fichier et télécharger le fichier.
+fr: > Si vous sauvegardez le fichier sour le nom `filename.lhs`, vous pouvez l'exécuter avec :
+fr: >
+fr: > runhaskell filename.lhs
+fr: >
+fr: >
+fr: > Certains ne marcheront pas, mais la majorité vous donneront un résultat.
+fr: > Vous devriez voir un lien juste en dessous.
+
+enddiv
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs
new file mode 100644
index 0000000..d1ddeed
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs
@@ -0,0 +1,76 @@
+
Introduction
+
+en:
Install
+fr:
Installation
+
+blogimage("Haskell-logo.png", "Haskell logo")
+
+en: There are different way to install Haskell, I would recommend to use
+fr: Aujourd'huil je considère que la manière la plus aisée d'installer Haskell est d'utiliser
+[`stack`](https://haskellstack.org).
+
+en: There are other way to install Haskell on your system you could visit,
+en: you can learn more about it by visiting
+fr: Il y a d'autres maniètres d'installer Haskell sur votre system,
+fr: vous pouvez en savoir plus en visitant
+[haskell.org](https://haskell.org)
+en: or
+fr: ou
+[haskell-lang.org](https://haskell-lang.org)
+
+en: Tools:
+fr: Outils:
+
+en: - `ghc`: Compiler similar to gcc for `C`.
+en: - `ghci`: Interactive Haskell (REPL)
+en: - `runhaskell`: Execute a program without compiling it. Convenient but very slow compared to compiled programs.
+fr: - `ghc`: Compilateur similaire à gcc pour le langage `C`.
+fr: - `ghci`: Console Haskell interactive (Read-Eval-Print Loop)
+fr: - `runhaskell`: Exécuter un programme sans le compiler. Pratique mais très lent comparé aux programmes compilés.
+
+en:
Don't be afraid
+fr:
Ne soyez pas effrayés!
+
+blogimage("munch_TheScream.jpg","The Scream")
+
+en: Many books/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc...).
+en: I will do the exact opposite.
+en: At first I won't show you any Haskell super power.
+en: I will start with similarities between Haskell and other programming languages.
+en: Let's jump to the mandatory "Hello World".
+fr: Beaucoup de livres/articles sur Haskell commencent par présenter des formules ésotériques (Algorithmes de tri rapide, suite de Fibonacci, etc...).
+fr: Je ferai l'exact opposé
+fr: En premier lieu je ne vous montrerai pas les super-pouvoirs d'Haskell.
+fr: Je vais commencer par les similarités avec les autres langages de programmation.
+fr: Commençons par l'indispensable "Hello World!".
+
+> main = putStrLn "Hello World!"
+
+en: To run it, you can save this code in a `hello.hs` and:
+fr: Pour l'exécuter, vous pouvez enregistrer ce code dans un fichier `hello.hs` et:
+
+
+~ runhaskell ./hello.hs
+Hello World!
+
+
+en: or if you use `stack` first run `stack setup` and then:
+fr: ou si vous utilisez `stack` lancez d'abord `stack setup` et ensuite :
+
+
+~ stack runhaskell ./hello.hs
+Hello World!
+
+
+
+en: You could also download the literate Haskell source.
+en: You should see a link just above the introduction title.
+en: Download this file as `00_hello_world.lhs` and:
+fr: Vous pouvez également télécharger la source Haskell littérale.
+fr: Vous devriez voir un lien juste au dessus du titre de l'introduction.
+fr: Téléchargez ce fichier en tant que `00_hello_world.lhs` et:
+
+
+~ runhaskell 00_hello_world.lhs
+Hello World!
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs
new file mode 100644
index 0000000..ec5ca13
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs
@@ -0,0 +1,52 @@
+en: Now, a program asking your name and replying "Hello" using the name you entered:
+fr: Maintenant, un programme qui demande votre nom et répond "Hello" suivit du nom que vous avez entré:
+
+> main = do
+> print "What is your name?"
+> name <- getLine
+> print ("Hello " ++ name ++ "!")
+
+en: First, let us compare this with similar programs in a few imperative languages:
+fr: Premièrement, comparons ce code avec ceux de quelques langages de programmation impératif:
+
+
+ # Python
+print "What is your name?"
+name = raw_input()
+print "Hello %s!" % name
+
+
+
+ # Ruby
+puts "What is your name?"
+name = gets.chomp
+puts "Hello #{name}!"
+
+
+
+// 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;
+}
+
+
+en: The structure is the same, but there are some syntax differences.
+en: The main part of this tutorial will be dedicated to explaining why.
+fr: La structure est la même, mais il y a quelques différences de syntaxe.
+fr: La partie principale de ce tutoriel sera consacrée à expliquer cela.
+
+en: In Haskell there is a `main` function and every object has a type.
+en: The type of `main` is `IO ()`.
+en: This means `main` will cause side effects.
+fr: En Haskell il y a une fonction `main` tous les objets ont un type.
+fr: Le type de `main` est `IO ()`.
+fr: Cela veut dire que `main` causera des effets secondaires.
+
+en: Just remember that Haskell can look a lot like mainstream imperative languages.
+fr: Rappelez-vous just que Haskell peut ressembler énormément aux principaux langages impératifs.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs
new file mode 100644
index 0000000..297e21b
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs
@@ -0,0 +1,159 @@
+en:
Very basic Haskell
+fr:
Les bases de Haskell
+
+blogimage("picasso_owl.jpg","Picasso minimal owl")
+
+en: Before continuing you need to be warned about some essential properties of Haskell.
+fr: Avant de continuer, vous devez êtres avertis à propos de propriétés essentielles de Haskell.
+
+en: _Functional_
+fr: _Fonctionnel_
+
+en: Haskell is a functional language.
+en: If you have an imperative language background, you'll have to learn a lot of new things.
+en: Hopefully many of these new concepts will help you to program even in imperative languages.
+fr: Haskell est un langage fonctionnel
+fr: Si vous avez déjà travaillé avec un langage impératif, vous devrez apprendre beaucoup de nouvelles choses.
+fr: Heureusement beaucoup de ces nouveaux concepts vous aidera à programmer même dans un langage impératif.
+
+en: _Smart Static Typing_
+fr: _Typage Statique Intelligent_
+
+en: Instead of being in your way like in `C`, `C++` or `Java`, the type system is here to help you.
+fr: Au lieu de bloquer votre chemin comme en `C`, `C++` ou `Java`, le système de typage est ici pour vous aider.
+
+en: _Purity_
+fr: _Pureté_
+
+en: Generally your functions won't modify anything in the outside world.
+en: 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.
+en: On the other hand, parallelism will be very easy to achieve.
+en: Haskell makes it clear where effects occur and where your code is pure.
+en: Also, it will be far easier to reason about your program.
+en: Most bugs will be prevented in the pure parts of your program.
+fr: Généralement vos fonctions ne modifieront rien du le monde extérieur.
+fr: Cela veut dire qu'elles ne peuvent pas modifier la valeur d'une variable,
+fr: lire du texte entré par un utilisateur,
+fr: écrire sur l'écran, lancer un missile.
+fr: D'un autre coté, avoir un code parallèle devient très facile.
+fr: Haskell rend très clair où les effets apparaissent et où le code est pur.
+fr: De plus, il devient beaucoup plus aisé de raisonner sur son programme.
+fr: La majorité des bugs seront évités dans les parties pures de votre programme.
+
+en: Furthermore, pure functions follow a fundamental law in Haskell:
+fr: En outre, les fonctions pures suivent une loi fondamentale en Haskell:
+
+en: > Applying a function with the same parameters always returns the same value.
+fr: > Appliquer une fonction avec les mêmes paramètres retourne toujours la même valeur.
+
+en: _Laziness_
+fr: _Paresse_
+
+en: Laziness by default is a very uncommon language design.
+en: By default, Haskell evaluates something only when it is needed.
+en: In consequence, it provides a very elegant way to manipulate infinite structures, for example.
+fr: La paresse par défaut est un choix de conception de langage très rare.
+fr: Par défaut, Haskell évalue quelque chose seulement lorsque cela est nécessaire.
+fr: En conséquence, cela fournit un moyen très élégant de manipuler des structures infinies, par exemple.
+
+en: A last warning about how you should read Haskell code.
+en: For me, it is like reading scientific papers.
+en: Some parts are very clear, but when you see a formula, just focus and read slower.
+en: Also, while learning Haskell, it _really_ doesn't matter much if you don't understand syntax details.
+en: If you meet a `>>=`, `<$>`, `<-` or any other weird symbol, just ignore them and follows the flow of the code.
+fr: Un dernier avertissement sur comment vous devriez lire le code Haskell.
+fr: Pour moi, c'est comme lire des papiers scientifiques.
+fr: Quelques parties sont très claires, mais quand vous voyez une formule, concentrez-vous dessus et lisez plus lentement.
+fr: De plus, lorsque vous apprenez Haskell, cela n'importe _vraiment_ pas si vous ne comprenez pas les détails syntaxiques.
+fr: Si vous voyez un `>>=`, `<$>`, `<-` ou n'importe quel symbole bizarre, ignorez-les et suivez le déroulement du code.
+
+en:
Function declaration
+fr:
Déclaration de fonctions
+
+en: You might be used to declaring functions like this:
+fr: Vous avez déjà dû déclarer des fonctions comme cela:
+
+en: In `C`:
+fr: En `C`:
+
+
+int f(int x, int y) {
+ return x*x + y*y;
+}
+
+
+en: In JavaScript:
+fr: En JavaScript:
+
+
+function f(x,y) {
+ return x*x + y*y;
+}
+
+
+en: in Python:
+fr: En Python:
+
+
+def f(x,y):
+ return x*x + y*y
+
+
+en: in Ruby:
+fr: En Ruby:
+
+
+def f(x,y)
+ x*x + y*y
+end
+
+
+en: In Scheme:
+fr: En Scheme:
+
+
+(define (f x y)
+ (+ (* x x) (* y y)))
+
+
+en: Finally, the Haskell way is:
+fr: Finalement, la manière de faire de Haskell est:
+
+
+f x y = x*x + y*y
+
+
+en: Very clean. No parenthesis, no `def`.
+fr: Très propre. Aucune parenthèse, aucun `def`.
+
+en: Don't forget, Haskell uses functions and types a lot.
+en: It is thus very easy to define them.
+en: The syntax was particularly well thought out for these objects.
+fr: N'oubliez pas, Haskell utilise beaucoup les fonctions et les types.
+fr: C'est très facile de les définir.
+fr: La syntaxe a été particulièrement réfléchie pour ces objets.
+
+en:
A Type Example
+fr:
Un exemple de type
+
+en: Although it is not mandatory, type information for functions is usually made
+en: explicit. It's not mandatory because the compiler is smart enough to discover
+en: it for you. It's a good idea because it indicates intent and understanding.
+fr: Même si ce n'est pas obligatoire, les informations de type pour les fonctions sont habituellement déclarées
+fr: explicitement. Ce n'est pas indispensable car le compilateur est suffisamment intelligent pour le déduire
+fr: à votre place. Cependant, c'est une bonne idée car cela montre bien l'intention du développeur et facilite la compréhension.
+
+en: Let's play a little.
+en: We declare the type using `::`
+fr: Jouons un peu.
+fr: On déclare le type en utilisant `::`
+
+> f :: Int -> Int -> Int
+> f x y = x*x + y*y
+>
+> main = print (f 2 3)
+
+~~~
+~ runhaskell 20_very_basic.lhs
+13
+~~~
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs
new file mode 100644
index 0000000..8d49e0c
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs
@@ -0,0 +1,23 @@
+en: Now try
+fr: Maintenant essayez
+
+> f :: Int -> Int -> Int
+> f x y = x*x + y*y
+>
+> main = print (f 2.3 4.2)
+
+en: You should get this error:
+fr: Vous devriez avoir cette erreur:
+
+~~~
+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)
+~~~
+
+en: The problem: `4.2` isn't an Int.
+fr: Le problème est que `4.2` n'est pas de type `Int` (_NDT: Il n'est pas un entier_)
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs
new file mode 100644
index 0000000..1ef0e2a
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs
@@ -0,0 +1,170 @@
+en: The solution: don't declare a type for `f` for the moment and let Haskell infer the most general type for us:
+fr: La soulution: ne déclarez pas de type pour `f` pour le moment et laissez Haskell inférer le type le plus général pour nous:
+
+> f x y = x*x + y*y
+>
+> main = print (f 2.3 4.2)
+
+en: It works!
+en: Luckily, we don't have to declare a new function for every single type.
+en: For example, in `C`, you'll have to declare a function for `int`, for `float`, for `long`, for `double`, etc...
+fr: Maintenant, ça marche!
+fr: Heureursement, nous n'avons pas à déclarer un nouvelle fonction pour chaque type différent.
+fr: Par exemple, en `C`, vous auriez dû déclarer un fonction pour `int`, pour `float`, pour `long`, pour `double`, etc...
+
+en: But, what type should we declare?
+en: To discover the type Haskell has found for us, just launch ghci:
+fr: Mais quel type devons nous déclarer?
+fr: Pour découvrir le type que Haskell a trouvé pour nous, lançons ghci:
+
+
+% 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
+
+
+en: Uh? What is this strange type?
+fr: Hein? Quel ce type étrange?
+
+~~~
+Num a => a -> a -> a
+~~~
+
+en: First, let's focus on the right part `a -> a -> a`.
+en: To understand it, just look at a list of progressive examples:
+fr: Premièrement, concentrons-nous sur la partie de droite: `a -> a -> a`.
+fr: Pour le comprendre, regardez cette liste d'exemples progressifs:
+
+en: --------------------------------------------------------------------------------------------------
+en: The written type Its meaning
+en: -------------------------- -----------------------------------------------------------------------
+en: `Int` the type `Int`
+en:
+en: `Int -> Int` the type function from `Int` to `Int`
+en:
+en: `Float -> Int` the type function from `Float` to `Int`
+en:
+en: `a -> Int` the type function from any type to `Int`
+en:
+en: `a -> a` the type function from any type `a` to the same type `a`
+en:
+en: `a -> a -> a` the type function of two arguments of any type `a` to the same type `a`
+en: --------------------------------------------------------------------------------------------------
+fr: --------------------------------------------------------------------------------------------------------------------------------------
+fr: Le type écrit Son sens
+fr: -------------------------- -----------------------------------------------------------------------------------------------------------
+fr: `Int` Le type `Int`
+fr:
+fr: `Int -> Int` Le type de la fonction qui prend un `Int` et retourne un `Int`
+fr:
+fr: `Float -> Int` Le type de la fonction qui prend un `Float` et retourne un `Int`
+fr:
+fr: `a -> Int` Le type de la fonction qui prend n'importe quel type de variable et retourne un `Int`
+fr:
+fr: `a -> a` Le type de la fonction qui prend n'importe quel type `a` et retourne une variable du même type `a`
+fr:
+fr: `a -> a -> a` Le type de la fonction qui prend de arguments de n'importe quel type`a` et retourne une variable de type `a`
+fr: --------------------------------------------------------------------------------------------------------------------------------------
+
+en: In the type `a -> a -> a`, the letter `a` is a _type variable_.
+en: It means `f` is a function with two arguments and both arguments and the result have the same type.
+en: The type variable `a` could take many different type values.
+en: For example `Int`, `Integer`, `Float`...
+fr: Dans le type `a -> a -> a`, la lettre `a` est une _variable de type_.
+fr: Cela signifie que `f` est une fonction avec deux arguments et que les deux arguments et le résultat ont le même type.
+fr: La variable de type `a` peut prendre de nombreuses valeurs différentes
+fr: Par exemple `Int`, `Integer`, `Float`...
+
+en: So instead of having a forced type like in `C` and having to declare a function
+en: for `int`, `long`, `float`, `double`, etc., we declare only one function like
+en: in a dynamically typed language.
+fr: Donc à la place d'avoir un type forcé comme en `C` et de devoir déclarer une fonction
+fr: pour `int`, `long`, `float`, `double`, etc., nous déclarons une seule fonction comme
+fr: dans un langage typé de façon dynamique.
+
+en: This is sometimes called parametric polymorphism. It's also called having your
+en: cake and eating it too.
+fr: C'est parfois appelé le polymorphisme paramétrique. C'est aussi appelé avoir un
+fr: gâteau et le manger.
+
+en: Generally `a` can be any type, for example a `String` or an `Int`, but also
+en: more complex types, like `Trees`, other functions, etc. But here our type is
+en: prefixed with `Num a => `.
+fr: Généralement `a` peut être de n'importe quel type, par exemple un `String` ou un `Int`, mais aussi
+fr: des types plus complexes comme `Trees`, d'autres fonctions, etc. Mais ici notre type est
+fr: préfixé par `Num a => `.
+
+en: `Num` is a _type class_.
+en: A type class can be understood as a set of types.
+en: `Num` contains only types which behave like numbers.
+en: More precisely, `Num` is class containing types which implement a specific list of functions, and in particular `(+)` and `(*)`.
+fr: `Num` est une _classe de type_.
+fr: Une classe de type peut être comprise comme un ensemble de types
+fr: `Num` contient seulement les types qui se comportent comme des nombres.
+fr: Plus précisement, `Num` est une classe qui contient des types qui implémentent une liste spécifique de fonctions,
+fr: en particulier `(+)` et `(*)`.
+
+en: Type classes are a very powerful language construct.
+en: We can do some incredibly powerful stuff with this.
+en: More on this later.
+fr: Les classes de types sont une structure de langage très puissante.
+fr: Nous pouvons faire des trucs incroyablement puissants avec.
+fr: Nous verrons cela plus tard.
+
+en: Finally, `Num a => a -> a -> a` means:
+fr: Finalement, `Num a => a -> a -> a` signifie:
+
+en: Let `a` be a type belonging to the `Num` type class.
+en: This is a function from type `a` to (`a -> a`).
+fr: soit `a` un type qui appartient à la classe `Num`.
+fr: C'est une fonction qui prend une variable de type `a` et retourne une fonction de type `(a -> a)`
+
+en: Yes, strange.
+en: In fact, in Haskell no function really has two arguments.
+en: Instead all functions have only one argument.
+en: 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.
+fr: Oui, c'est étrange.
+fr: En fait, en Haskell aucune fonction ne prend réellement deux arguments.
+fr: Au lieu de cela toutes les fonctions n'ont qu'un argument unique.
+fr: Mais nous retiendrons que prendre deux arguments est équivalent à n'en prendre qu'un et à retourner une fonction qui prend le second argument en paramètre.
+
+en: More precisely `f 3 4` is equivalent to `(f 3) 4`.
+en: Note `f 3` is a function:
+fr: Plus précisement `f 3 4` est équivalent à `(f 3) 4 `.
+fr: Remarque: `f 3` est une fonction:
+
+~~~
+f :: Num a => a -> a -> a
+
+g :: Num a => a -> a
+g = f 3
+
+g y ⇔ 3*3 + y*y
+~~~
+
+en: Another notation exists for functions.
+en: The lambda notation allows us to create functions without assigning them a name.
+en: We call them anonymous functions.
+en: We could also have written:
+fr: Une autre notation existe pour les fonctions.
+fr: La notation lambda nous autorise à créer des fonctions sans leur assigner un nom.
+fr: On les appelle des fonctions anonymes.
+fr: nous aurions donc pu écrire:
+
+~~~
+g = \y -> 3*3 + y*y
+~~~
+
+en: The `\` is used because it looks like `λ` and is ASCII.
+fr: Le `\` esst utilisé car il ressemble à un `λ` et est un caractère ASCII.
+
+en: If you are not used to functional programming your brain should be starting to heat up.
+en: It is time to make a real application.
+fr: Si vous n'êtes pas habitué à la programmation fonctionnelle, votre cerveau devrait commencer à chauffer
+fr: Il est temps de faire une vraie application.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs
new file mode 100644
index 0000000..f6881e3
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs
@@ -0,0 +1,12 @@
+en: But just before that, we should verify the type system works as expected:
+fr: Mais juste avant cela, nous devrions vérifier que le système de type marche comme nous le supposons:
+
+> f :: Num a => a -> a -> a
+> f x y = x*x + y*y
+>
+> main = print (f 3 2.4)
+
+en: It works, because, `3` is a valid representation both for Fractional numbers like Float and for Integer.
+en: As `2.4` is a Fractional number, `3` is then interpreted as being also a Fractional number.
+fr: Cela fonctionne, car `3` est une représentation valide autant pour les nombres fractionnaires comme Float que pour les entiers.
+fr: Comme `2.4` est un nombre fractionnaire, `3` est interprété comme une autre nombre fractionnaire
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs
new file mode 100644
index 0000000..8512c6b
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs
@@ -0,0 +1,24 @@
+en: If we force our function to work with different types, it will fail:
+fr: Si nous forçons notre fonction à travailler avec des types différents, le test échouera:
+
+> 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)
+
+en: The compiler complains.
+en: The two parameters must have the same type.
+fr: Le compilateur se plaint.
+fr: Les deux paramètres doivent avoir le même type.
+
+en: If you believe that this is a bad idea, and that the compiler should make the transformation
+en: from one type to another for you, you should really watch this great (and funny) video:
+en: [WAT](https://www.destroyallsoftware.com/talks/wat)
+fr: Si vous pensez que c'est une mauvaise idée et que le compilateur devrait faire la transformation
+fr: depuis un type à un autre pour vous, vous devriez vraiment regarder cette vidéo géniale (et amusante):
+fr: [WAT](https://www.destroyallsoftware.com/talks/wat) (_NDT: En Anglais_)
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs
new file mode 100644
index 0000000..7744b8d
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs
@@ -0,0 +1,171 @@
+en:
Essential Haskell
+fr:
Notions essentielles
+
+blogimage("kandinsky_gugg.jpg","Kandinsky Gugg")
+
+en: I suggest that you skim this part.
+en: Think of it as a reference.
+en: Haskell has a lot of features.
+en: A lot of information is missing here.
+en: Come back here if the notation feels strange.
+fr: Je vous suggère de seulement survoler cette partie
+fr: Pensez-y seulement comme à une référence.
+fr: Haskell a beaucoup de caractèristiques
+fr: Il manque beaucoup d'informations ici.
+fr: Revenz ici si la notation vous semble étrange.
+
+en: I use the `⇔` symbol to state that two expression are equivalent.
+en: It is a meta notation, `⇔` does not exists in Haskell.
+en: I will also use `⇒` to show what the return value of an expression is.
+fr: J'utilise le symbole `⇔` pour signifier que deux expressions sont équivalentes.
+fr: C'est une notation extérieure, `⇔` n'existe pas en Haskell.
+fr: Je vais aussi utiliser le symoble `⇒` quelle est la valeur que retourne une fonction.
+
+
Notations
+
+en:
Arithmetic
+fr:
Arithmétique
+
+~~~
+3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3)
+~~~
+
+en:
Logic
+fr:
Logique
+
+~~~
+True || False ⇒ True
+True && False ⇒ False
+True == False ⇒ False
+en: True /= False ⇒ True (/=) is the operator for different
+fr: True /= False ⇒ True (/=) est l'opérateur pour "différent de"
+~~~
+
+en:
Powers
+fr:
Puissances
+
+~~~
+en: x^n for n an integral (understand Int or Integer)
+en: x**y for y any kind of number (Float for example)
+fr: x^n pour n un entier (comprenez Int ou Integer)
+fr: x**y pour y tout type de nombre (Float par exemple)
+~~~
+
+en: `Integer` has no limit except the capacity of your machine:
+fr: `Integer` n'a aucune limite à part la capacité de votre machine:
+
+~~~
+4^103
+102844034832575377634685573909834406561420991602098741459288064
+~~~
+
+Yeah!
+en: And also rational numbers FTW!
+en: But you need to import the module `Data.Ratio`:
+fr: Et aussi les nombres rationnels!
+fr: Mais vous avez besoin d'importer le module `Data.Ratio`
+
+~~~
+$ ghci
+....
+Prelude> :m Data.Ratio
+Data.Ratio> (11 % 15) * (5 % 3)
+11 % 9
+~~~
+
+en:
Lists
+fr:
Listes
+
+~~~
+en: [] ⇔ empty list
+en: [1,2,3] ⇔ List of integral
+en: ["foo","bar","baz"] ⇔ List of String
+en: 1:[2,3] ⇔ [1,2,3], (:) prepend one element
+en: 1:2:[] ⇔ [1,2]
+en: [1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concatenate
+en: [1,2,3] ++ ["foo"] ⇔ ERROR String ≠ Integral
+en: [1..4] ⇔ [1,2,3,4]
+en: [1,3..10] ⇔ [1,3,5,7,9]
+en: [2,3,5,7,11..100] ⇔ ERROR! I am not so smart!
+en: [10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1]
+fr: [] ⇔ liste vide
+fr: [1,2,3] ⇔ Liste d'entiers
+fr: ["foo","bar","baz"] ⇔ Liste de chaînes de caractères
+fr: 1:[2,3] ⇔ [1,2,3], (:) ajoute un élément au début
+fr: 1:2:[] ⇔ [1,2]
+fr: [1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concaténation de deux listes
+fr: [1,2,3] ++ ["foo"] ⇔ ERREUR String ≠ Integral
+fr: [1..4] ⇔ [1,2,3,4]
+fr: [1,3..10] ⇔ [1,3,5,7,9]
+fr: [2,3,5,7,11..100] ⇔ ERREUR! Je ne suis pas si intelligent!
+fr: [10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1]
+~~~
+
+en:
Strings
+fr:
Chaînes de caractères
+
+en: In Haskell strings are list of `Char`.
+fr: En Haskell les chaînes de caractères sont des listes de `Char`.
+
+~~~
+'a' :: Char
+"a" :: [Char]
+"" ⇔ []
+"ab" ⇔ ['a','b'] ⇔ 'a':"b" ⇔ 'a':['b'] ⇔ 'a':'b':[]
+"abc" ⇔ "ab"++"c"
+~~~
+
+en: > _Remark_:
+en: > In real code you shouldn't use list of char to represent text.
+en: > You should mostly use `Data.Text` instead.
+en: > If you want to represent a stream of ASCII char, you should use `Data.ByteString`.
+fr: > _Remarque_:
+fr: > Dans un vrai code vous n'utiliserez pas des listes de char pour représenter du texte.
+fr: > Vous utiliserez plus souvent `Data.Text` à la place.
+fr: > Si vous voulez représenter un chapelet de caractères ASCII, vous utiliserez `Data.ByteString`.
+
+
Tuples
+
+en: The type of couple is `(a,b)`.
+en: Elements in a tuple can have different types.
+fr: Le type d'un couple est `(a,b)`.
+fr: Les éléments d'un tuple peuvent avoir des types différents.
+
+~~~
+en: -- All these tuples are valid
+fr: -- tous ces tuples sont valides
+(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
+~~~
+
+en:
Deal with parentheses
+fr:
Traiter avec les parenthèses
+
+en: To remove some parentheses you can use two functions: `($)` and `(.)`.
+fr: Pour enlever des parenthèses vous pouvez utiliser deux fonctions: `($)` et `(.)`.
+
+~~~
+en: -- By default:
+fr: -- Par défaut:
+f g h x ⇔ (((f g) h) x)
+
+en: -- the $ replace parenthesis from the $
+en: -- to the end of the expression
+fr: -- le $ remplace les parenthèses depuis le $
+fr: -- jusqu'à la fin de l'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))
+
+en: -- (.) the composition function
+fr: -- (.) permet de faire des compositions de fonctions
+(f . g) x ⇔ f (g x)
+(f . g . h) x ⇔ f (g (h x))
+~~~
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs
new file mode 100644
index 0000000..5d543b2
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs
@@ -0,0 +1,99 @@
+en:
Useful notations for functions
+fr:
Notations utiles pour les fonctions
+
+en: Just a reminder:
+fr: Juste un mémo:
+
+~~~
+en: x :: Int ⇔ x is of type Int
+en: x :: a ⇔ x can be of any type
+en: x :: Num a => a ⇔ x can be any type a
+en: such that a belongs to Num type class
+en: f :: a -> b ⇔ f is a function from a to b
+en: f :: a -> b -> c ⇔ f is a function from a to (b→c)
+en: f :: (a -> b) -> c ⇔ f is a function from (a→b) to c
+fr: x :: Int ⇔ x est de type Int
+fr: x :: a ⇔ x peut être de n'importe quel type
+fr: x :: Num a => a ⇔ x peut être de n'importe quel type a
+fr: tant qu' a appartient à la classe de type Num
+fr: f :: a -> b ⇔ f est une fonction qui prend un a et retourne un b
+fr: f :: a -> b -> c ⇔ f est une fonction qui prend un a et retourne un (b→c)
+fr: f :: (a -> b) -> c ⇔ f est une fonction qui prend un (a→b) et retourne un c
+~~~
+
+en: Remember that defining the type of a function before its declaration isn't mandatory.
+en: Haskell infers the most general type for you.
+en: But it is considered a good practice to do so.
+fr: Rappelez-vous que définir le type d'une fonction avant sa déclaration n'est pas obligatoire.
+fr: Haskell infère le type le plus général pour vous.
+fr: Mais c'est considéré comme une bonne pratique.
+
+en: _Infix notation_
+fr: _Notation Infixée_
+
+> square :: Num a => a -> a
+> square x = x^2
+
+en: Note `^` uses infix notation.
+en: For each infix operator there its associated prefix notation.
+en: You just have to put it inside parenthesis.
+fr: Remarquez que `^` utilise une notation infixée.
+fr: Pour chaque opérateur infixe il y a une notation préfixée associée.
+fr: Vous devz juste l'écrire entre parenthèses.
+
+> square' x = (^) x 2
+>
+> square'' x = (^2) x
+
+en: We can remove `x` in the left and right side!
+en: It's called η-reduction.
+fr: Nous pouvons enlever le `x` dans les parties de gauche et de droite!
+fr: On appelle cela la η-réduction
+
+> square''' = (^2)
+
+en: Note we can declare functions with `'` in their name.
+en: Here:
+fr: Rmarquez qu nous pouvons déclarer des fonctions avec `'` dans leur nom.
+fr: Exemples:
+
+ > `square` ⇔ `square'` ⇔ `square''` ⇔ `square'''`
+
+_Tests_
+
+en: An implementation of the absolute function.
+fr: Une implémentation de la fonction absolue.
+
+> absolute :: (Ord a, Num a) => a -> a
+> absolute x = if x >= 0 then x else -x
+
+en: Note: the `if .. then .. else` Haskell notation is more like the
+en: `¤?¤:¤` C operator. You cannot forget the `else`.
+fr: Remarque: la notation de Haskell pour le `if .. then .. else` ressemble plus
+fr: à l'opérateur `¤?¤:¤` en C. Le `else` est obligatoire.
+
+en: Another equivalent version:
+fr: Une version équivalente:
+
+> absolute' x
+> | x >= 0 = x
+> | otherwise = -x
+
+en: > Notation warning: indentation is _important_ in Haskell.
+en: > Like in Python, bad indentation can break your code!
+fr: > Avertissement: l'indentation est _importante_ en Haskell.
+fr: > Comme en Python, une mauvaise indentation peut détruire votre code!
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs
new file mode 100644
index 0000000..02df0e4
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs
@@ -0,0 +1,133 @@
+en:
Hard Part
+fr:
La Partie Difficile
+
+en: The hard part can now begin.
+fr: La partie difficile peut maintenant commencer.
+
+en:
Functional style
+fr:
Le style fonctionnel
+
+blogimage("hr_giger_biomechanicallandscape_500.jpg","Biomechanical Landscape by H.R. Giger")
+
+en: In this section, I will give a short example of the impressive refactoring ability provided by Haskell.
+en: We will select a problem and solve it in a standard imperative way.
+en: Then I will make the code evolve.
+en: The end result will be both more elegant and easier to adapt.
+fr: Dans cette section, je vais vous donner un court exemple de l'impressionante capacité de remaniement de Haskell.
+fr: Nous allons sélectionner un problème et le résoudre à la manière d'un langage impératif standard.
+fr: Ensuite, je ferais évoluer le code.
+fr: Le résultat final sera plus élégant et plus facile à adapter.
+
+en: Let's solve the following problem:
+fr: résolvons les problèmes suivants:
+
+en: > Given a list of integers, return the sum of the even numbers in the list.
+fr: > Soit une liste d'entiers, retourner la somme des nombres pairs de cette liste.
+ >
+en: > example:
+fr: > exemple:
+ > `[1,2,3,4,5] ⇒ 2 + 4 ⇒ 6`
+
+en: To show differences between functional and imperative approaches,
+en: I'll start by providing an imperative solution (in JavaScript):
+fr: Pour montrer les différences entre les approches fonctionnelle et impérative,
+fr: je vais commencer par donner la solution impérative (en JavaScript):
+
+
+function evenSum(list) {
+ var result = 0;
+ for (var i=0; i< list.length ; i++) {
+ if (list[i] % 2 ==0) {
+ result += list[i];
+ }
+ }
+ return result;
+}
+
+
+en: In Haskell, by contrast, we don't have variables or a for loop.
+en: One solution to achieve the same result without loops is to use recursion.
+fr: En Haskell, en revanche, nous n'avons pas de variables ou un boucle `for`.
+fr: Une des solutions pour parvenir au même résultat sans boucles est d'utiliser la récursion.
+
+en: > _Remark_:
+en: > Recursion is generally perceived as slow in imperative languages.
+en: > But this is generally not the case in functional programming.
+en: > Most of the time Haskell will handle recursive functions efficiently.
+fr: > _Remarque_:
+fr: > La récursion est souvent perçue comme lente dans les langages impératifs.
+fr: > Mais ce n'est généralement pas le cas en programmation fonctionnelle.
+fr: > La plupart du temps Haskell gérera les fonctions récursives efficacement.
+
+en: Here is a `C` version of the recursive function.
+en: Note that for simplicity I assume the int list ends with the first `0` value.
+fr: Voici la version `C` de la fonction récursive.
+fr: Remarquez que je suppose que la liste d'int fini avec la première valeur `0`.
+
+
+
+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);
+ }
+ }
+}
+
+
+en: Keep this code in mind. We will translate it into Haskell.
+en: First, however, I need to introduce three simple but useful functions we will use:
+fr: Gardez ce code à l'esprit. Nous allons le traduire en Haskell.
+fr: Premièrement,
+
+
+even :: Integral a => a -> Bool
+head :: [a] -> a
+tail :: [a] -> [a]
+
+
+en: `even` verifies if a number is even.
+fr: `even` vérifie si un nombre est pair.
+
+
+even :: Integral a => a -> Bool
+even 3 ⇒ False
+even 2 ⇒ True
+
+
+en: `head` returns the first element of a list:
+fr: `head` retourne le premier élément d'une liste:
+
+
+head :: [a] -> a
+head [1,2,3] ⇒ 1
+head [] ⇒ ERROR
+
+
+en: `tail` returns all elements of a list, except the first:
+fr: `tail` retourne tous les éléments d'une liste, sauf le premier:
+
+
+tail :: [a] -> [a]
+tail [1,2,3] ⇒ [2,3]
+tail [3] ⇒ []
+en: tail [] ⇒ ERROR
+fr: tail [] ⇒ ERREUR
+
+
+en: Note that for any non empty list `l`,
+en: `l ⇔ (head l):(tail l)`
+fr: Remarquez que pour toute liste non-vide `l`,
+fr: `l ⇔ (head l):(tail l)`
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs
new file mode 100644
index 0000000..cc34f13
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs
@@ -0,0 +1,80 @@
+en: The first Haskell solution.
+en: The function `evenSum` returns the sum of all even numbers in a list:
+fr: La première solution en Haskell.
+fr: La fonction `evenSum` retourne la somme de tous les nombres pairs d'une liste:
+
+> -- 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
+
+en: To test a function you can use `ghci`:
+fr: Pour tester une fonction nous pouvons utiliser `ghci`:
+
+
+% 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
+
+
+en: Here is an example of execution[^2]:
+fr: Voici un exemple d'exécution[^2]:
+
+en: [^2]: I know I'm cheating. But I will talk about non-strictness later.
+fr: [^2]: Je sais que je triche. Mais je parlerais de la non-rigueur plus tard.
+
+
+*Main> evenSum [1..5]
+accumSum 0 [1,2,3,4,5]
+en: 1 is odd
+fr: 1 est impair
+accumSum 0 [2,3,4,5]
+en: 2 is even
+fr: 2 est pair
+accumSum (0+2) [3,4,5]
+en: 3 is odd
+fr: 3 est impair
+accumSum (0+2) [4,5]
+en: 2 is even
+fr: 4 est pair
+accumSum (0+2+4) [5]
+en: 5 is odd
+fr: 5 est impair
+accumSum (0+2+4) []
+l == []
+0+2+4
+0+6
+6
+
+
+en: Coming from an imperative language all should seem right.
+en: In fact, many things can be improved here.
+en: First, we can generalize the type.
+fr: En venant d'un langage impératif, tout devrait vous sembler juste.
+fr: En fait, beaucoup de choses peuvent être améliorées ici.
+fr: Tout d'abord, nous pouvons généraliser le type.
+
+
+evenSum :: Integral a => [a] -> a
+
+
+
+
+> main = do print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs
new file mode 100644
index 0000000..8907b1e
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs
@@ -0,0 +1,23 @@
+en: Next, we can use sub functions using `where` or `let`.
+fr: Ensuite, nous pouvons utiliser des sous-fonctions grâce à `where` et `let`.
+en: This way our `accumSum` function won't pollute the namespace of our module.
+fr: Ansi, notre fonction `accumSum` ne polluera pas le _namespace_ de notre module
+
+> -- 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
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs
new file mode 100644
index 0000000..a2849c4
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs
@@ -0,0 +1,64 @@
+en: Next, we can use pattern matching.
+fr: Puis on utilise le _pattern matching_
+
+> -- 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
+
+en: What is pattern matching?
+en: Use values instead of general parameter names[^021301].
+fr: Qu'est ce que le _pattern matching_ ?
+fr: Il s'agit d'utiliser des valeurs au lieu de noms de paramètres généraux.
+
+en: [^021301]: For the brave, a more complete explanation of pattern matching can be found [here](http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html).
+fr: [^021301]: Pour les plus courageux, une explication plus complète du _pattern matching_ peut être trouvée [ici](http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html) (_NdT: En anglais_)
+
+en: Instead of saying: `foo l = if l == [] then else `
+en: You simply state:
+fr: Au lieu d'écrire: `foo l = if l == [] then else `
+fr: Vous écrivez tout simplement :
+
+
+foo [] =
+foo l =
+
+
+en: But pattern matching goes even further.
+en: It is also able to inspect the inner data of a complex value.
+en: We can replace
+fr: Mais le _pattern matching_ peut aller encore plus loin.
+fr: Il est également capable d'inspect les données internes d'un valeur complexe.
+fr: Nous pouvons ainsi remplacer
+
+
+foo l = let x = head l
+ xs = tail l
+ in if even x
+ then foo (n+x) xs
+ else foo n xs
+
+
+en: with
+fr: par
+
+
+foo (x:xs) = if even x
+ then foo (n+x) xs
+ else foo n xs
+
+
+en: This is a very useful feature.
+en: It makes our code both terser and easier to read.
+fr: C'est une caractéristique très utile.
+fr: Notre code est ainsi plus concis et plus facile à lire.
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs
new file mode 100644
index 0000000..9a92064
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs
@@ -0,0 +1,37 @@
+en: In Haskell you can simplify function definitions by η-reducing them.
+en: For example, instead of writing:
+fr: Avec Haskell, nous pouvons simplifier les défitions des fonctions en les _η-réduisant_ .
+fr: Par exemple, au lieu d'écrire:
+
+
+en: f x = (some expresion) x
+fr: f x = (expression) x
+
+
+en: you can simply write
+fr: Nous pouvons écrire
+
+
+en: f = some expression
+fr: f = expression
+
+
+en: We use this method to remove the `l`:
+fr: Utilisons cette méthode pour retirer le `l`:
+
+> -- 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
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs
new file mode 100644
index 0000000..6007a34
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs
@@ -0,0 +1,123 @@
+en:
Higher Order Functions
+fr:
Fonctions d'ordre supérieur
+
+blogimage("escher_polygon.png","Escher")
+
+en: To make things even better we should use higher order functions.
+en: What are these beasts?
+en: Higher order functions are functions taking functions as parameters.
+fr: Pour rendre les choses plus faciles, nous devrions utiliser des fonctions d'ordre supérieur.
+fr: Ce sont des fonctions qui prennent des fonctions en paramètres
+
+en: Here are some examples:
+fr: Voici quelques exemples:
+
+
+filter :: (a -> Bool) -> [a] -> [a]
+map :: (a -> b) -> [a] -> [b]
+foldl :: (a -> b -> a) -> a -> [b] -> a
+
+
+en: Let's proceed by small steps.
+fr: Procédons par étapes.
+
+
+-- Version 5
+evenSum l = mysum 0 (filter even l)
+ where
+ mysum n [] = n
+ mysum n (x:xs) = mysum (n+x) xs
+
+
+en: where
+fr: où
+
+
+filter even [1..10] ⇔ [2,4,6,8,10]
+
+
+en: The function `filter` takes a function of type (`a -> Bool`) and a list of type `[a]`.
+en: It returns a list containing only elements for which the function returned `true`.
+fr: La fonction `filter` prend une fonction du type (`a -> Bool`) et une liste de type `[a]`.
+fr: Elle retourne une liste qui contient seulement les élements pour qui la fonction a retourné `True`.
+
+en: Our next step is to use another technique to accomplish the same thing as a loop.
+en: We will use the `foldl` function to accumulate a value as we pass through the list.
+en: The function `foldl` captures a general coding pattern:
+fr: La prochaine étape est d'utiliser une autre technique pour accomplir la même chose qu'une boucle.
+fr: Nous allons utiliser la fonction `foldl` pour accumuler une valeur au fur et à mesure que l'on parcoure la liste.
+fr: La fonction `foldl` capture un modèle de code général:
+
+
+
+en: Which can be replaced by:
+fr: Qui peut être remplacé par:
+
+
+myfunc list = foldl barinitialValuelist
+
+
+en: If you really want to know how the magic works, here is the definition of `foldl`:
+fr: Si vous souhaitez vraiment savoir comment la magie se produit, voici la définition de `foldl`:
+
+
+foldl f z [] = z
+foldl f z (x:xs) = foldl f (f z x) xs
+
+
+
+foldl f z [x1,...xn]
+⇔ f (... (f (f z x1) x2) ...) xn
+
+
+en: But as Haskell is lazy, it doesn't evaluate `(f z x)` and simply pushes it onto the stack.
+en: This is why we generally use `foldl'` instead of `foldl`;
+en: `foldl'` is a _strict_ version of `foldl`.
+en: If you don't understand what lazy and strict means,
+en: don't worry, just follow the code as if `foldl` and `foldl'` were identical.
+fr: Mais comme Haskell est paresseux, il n'évalue pas `(f z x)` et le met simplement dans la pile.
+fr: C'est pourquoi on utilise généralement `foldl'`, une version _stricte_ de `foldl`,
+fr: Si vous ne comprenez pas encore ce que _paresseux_ ou _strict_ signifie,
+fr: ne vous inquiétez pas, suivez le code comme si `foldl'` et `foldl` étaient identiques
+
+en: Now our new version of `evenSum` becomes:
+fr: Maintenant notre version de `evenSum` devient:
+
+
+-- Version 6
+en: -- foldl' isn't accessible by default
+en: -- we need to import it from the module Data.List
+fr: -- foldl' n'est pas accessible par défaut
+fr: -- nous devons l'importer depuis le module Data.List
+import Data.List
+evenSum l = foldl' mysum 0 (filter even l)
+ where mysum acc value = acc + value
+
+
+en: We can also simplify this by using directly a lambda notation.
+en: This way we don't have to create the temporary name `mysum`.
+fr: Nous pouvons aussi simplifier cela en utilisant une _lambda-notation_.
+fr: Ainsi nous n'avons pas besoin de créer le nom temporaire `mySum`.
+
+> -- 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)
+
+en: And of course, we note that
+fr: Et bien sûr, nous remarquons que
+
+
+(\x y -> x+y) ⇔ (+)
+
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs
new file mode 100644
index 0000000..fc1c1c1
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs
@@ -0,0 +1,148 @@
+en: Finally
+fr: Finalement
+
+
+-- Version 8
+import Data.List (foldl')
+evenSum :: Integral a => [a] -> a
+evenSum l = foldl' (+) 0 (filter even l)
+
+
+en: `foldl'` isn't the easiest function to grasp.
+en: If you are not used to it, you should study it a bit.
+fr: `foldl'` n'est pas la fonction la plus facile à prendre en main.
+fr: Si vous n'y êtes pas habitué, vous devriez l'étudier un peu.
+
+en: To help you understand what's going on here, let's look at a step by step evaluation:
+fr: Pour mieux comprendre ce qui se passe ici, étudions une évaluation étape par étape:
+
+
+
+
+en: Another useful higher order function is `(.)`.
+en: The `(.)` function corresponds to mathematical composition.
+fr: Une autre fonction d'ordre supérieur utile est `(.)`.
+fr: Elle correspond à une composition en mathématiques.
+
+
+(f . g . h) x ⇔ f ( g (h x))
+
+
+en: We can take advantage of this operator to η-reduce our function:
+fr: Nous pouvons profiter de cet opérateur pour η-réduire notre fonction:
+
+
+-- Version 9
+import Data.List (foldl')
+evenSum :: Integral a => [a] -> a
+evenSum = (foldl' (+) 0) . (filter even)
+
+
+en: Also, we could rename some parts to make it clearer:
+fr: Nous pouvons maintenant renommer certaines parties pour rendre le tout plus clair:
+
+> -- Version 10
+> import Data.List (foldl')
+> sum' :: (Num a) => [a] -> a
+> sum' = foldl' (+) 0
+> evenSum :: Integral a => [a] -> a
+> evenSum = sum' . (filter even)
+>
+
+en: It is time to discuss the direction our code has moved as we introduced more functional idioms.
+en: What did we gain by using higher order functions?
+fr: Il est temps de discuter de la direction qu'a pris notre code depuis que nous avons introduit plus d'idiomes fonctionnels.
+fr: Que gagnons-nous à utiliser des fonctions d'ordre supérieur?
+
+en: At first, you might think the main difference is terseness. But in fact, it has
+en: more to do with better thinking. Suppose we want to modify our function
+en: slightly, for example, to get the sum of all even squares of elements of the list.
+fr: D'abord, vous pourriez penser que la principale différence est la brièveté. Mais en réalité,
+fr: il s'agit d'une meilleure façon de penser. Supposons que nous voulons modifier légèrement notre fonction,
+fr: par exemple, pour qu'elle renvoie la somme de tous les carrés pairs des éléments de la liste.
+
+~~~
+[1,2,3,4] ▷ [1,4,9,16] ▷ [4,16] ▷ 20
+~~~
+
+en: Updating version 10 is extremely easy:
+fr: Mettre la version 10 à jour est très facile:
+
+> squareEvenSum = sum' . (filter even) . (map (^2))
+> squareEvenSum' = evenSum . (map (^2))
+
+en: We just had to add another "transformation function"[^0216].
+fr: Nous avons juste eu à ajouter une autre "fonction de trabsformation"[^0216].
+
+~~~
+map (^2) [1,2,3,4] ⇔ [1,4,9,16]
+~~~
+
+en: The `map` function simply applies a function to all the elements of a list.
+fr: La fonction `map` applique simplementune fonction à tous les élements d'une liste.
+
+en: We didn't have to modify anything _inside_ the function definition.
+en: This makes the code more modular.
+en: But in addition you can think more mathematically about your function.
+en: You can also use your function interchangably with others, as needed.
+en: That is, you can compose, map, fold, filter using your new function.
+fr: Nous n'avons rien modifié _à l'intérieur_ de notre définition de fonction.
+fr: Cela rend le code plus modulaire.
+fr: En plus de cela, vous pouvez penser à votre fonction plus mathématiquement.
+fr: Vous pouvez aussi utilier votre fonction avec d'autres, au besoin:
+fr: vous pouvez utiliser `compose`, `map`, `fold` ou `filter` sur notre nouvelle fonction.
+
+en: Modifying version 1 is left as an exercise to the reader ☺.
+fr: Modifier la version 1 est laissé comme un exercice pour le lecteur ☺.
+
+en: If you believe we have reached the end of generalization, then know you are very wrong.
+en: For example, there is a way to not only use this function on lists but on any recursive type.
+en: If you want to know how, I suggest you to read this quite fun article: [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).
+fr: Si vous croyez avoir atteint le bout de la généralisation, vous avez tout faux.
+fr: Par example, il y a un moyen d'utiliser cette fonction non seulement sur les listes mais aussi sur n'importe quel type récursif.
+fr: Si vous voulez savoir comment, je vous suggère de lire cet article: [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) (_NDT: en anglais, mais là vous vous en seriez douté je pense ☺_)
+
+en: This example should show you how great pure functional programming is.
+en: Unfortunately, using pure functional programming isn't well suited to all usages.
+en: Or at least such a language hasn't been found yet.
+fr: Cet exemple montre à quel point la programmation fonctionnelle pure est géniale.
+fr: Malheureusement, utiliser cet outil n'est pas adapté à tous les besoins.
+fr: Ou alors un langage qui le premettrait n'a pas encore été trouvé.
+
+en: One of the great powers of Haskell is the ability to create DSLs
+en: (Domain Specific Language)
+en: making it easy to change the programming paradigm.
+fr: Une des grands pouvoirs de Haskell est sa capacité à créer des DSLs
+fr: (_Domain Specific Language_, en français : _langage spécifique à un domaine_)
+fr: Il est ainsi facile de changer le pardigme de programmation
+
+en: In fact, Haskell is also great when you want to write imperative style
+en: programming. Understanding this was really hard for me to grasp when first
+en: learning Haskell. A lot of effort tends to go into explaining the superiority
+en: of the functional approach. Then when you start using an imperative style with
+en: Haskell, it can be hard to understand when and how to use it.
+fr: En fait, Haskell peut très bien vous permettre d'écrire des programmes impératifs.
+fr: Comprendre cela a été très difficile pour moi lorsque j'apprenais Haskell.
+fr: Beaucoup d'efforts tendent à expliquer la supériorité de l'approche fonctionnele.
+fr: Puis lorsque vous commencez à utliser le style impératif en Haskell,
+fr: Il peut être difficile de comprendre quand et où l'utliser.
+
+en: But before talking about this Haskell super-power, we must talk about another
+en: essential aspect of Haskell: _Types_.
+fr: Mais avant de parler de ce super-pouvoir de Haskell, nous devons parler
+fr: d'un autre aspet essentiel: les _Types_.
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs
new file mode 100644
index 0000000..175db29
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs
@@ -0,0 +1,137 @@
+en:
Types
+fr:
Les types
+
+blogimage("salvador-dali-the-madonna-of-port-lligat.jpg","Dali, the madonna of port Lligat")
+
+ > %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.
+
+en: In Haskell, types are strong and static.
+fr: En Haskell, les types sont forts et statiques.
+
+en: Why is this important? It will help you _greatly_ to avoid mistakes.
+en: In Haskell, most bugs are caught during the compilation of your program.
+en: And the main reason is because of the type inference during compilation.
+en: Type inference makes it easy to detect where you used the wrong parameter at the wrong place, for example.
+fr: Pourquoi est-ce important? Cela vous aidera a éviter _beaucoup_ d'erreurs.
+fr: En Haskell, la majorité des bugs est repérée durant la compilation de votre programme.
+fr: Et la raison principale de cela est l'inférence de type durant la compilation.
+fr: L'inférence de type permet de détecter plus facilement lorsque vous utilisez le mauvais paramètre au mauvais endroit, par exemple
+
+en:
Type inference
+fr:
Inférence de type
+
+en: Static typing is generally essential for fast execution.
+en: But most statically typed languages are bad at generalizing concepts.
+en: Haskell's saving grace is that it can _infer_ types.
+fr: Le typage statique est généralement essentiel pour une exécution rapide.
+fr: Mais la plupart des langages typés statiquement ont du mal à généraliser des concepts.
+fr: La "grâce salvatrice" de Haskell est qu'il peut _inférer_ des types.
+
+en: Here is a simple example, the `square` function in Haskell:
+fr: Voici un exemple simple, la fonction `square` en Haskell:
+
+
+square x = x * x
+
+
+en: This function can `square` any Numeral type.
+en: You can provide `square` with an `Int`, an `Integer`, a `Float` a `Fractional` and even `Complex`. Proof by example:
+fr: Cette fonction peut mettre au carré n'importe quel type `Numeral`.
+fr: Vous pouvez l'utilser avec un `Int`, un `Integer`, un `Float`, un `Fractional` ou même un `Complex`. Preuve par l'exemple:
+
+~~~
+% ghci
+GHCi, version 7.0.4:
+...
+Prelude> let square x = x*x
+Prelude> square 2
+4
+Prelude> square 2.1
+4.41
+en: Prelude> -- load the Data.Complex module
+fr: Prelude> -- charge le module Data.Complex
+Prelude> :m Data.Complex
+Prelude Data.Complex> square (2 :+ 1)
+3.0 :+ 4.0
+~~~
+
+en: `x :+ y` is the notation for the complex (x + iy).
+fr: `x :+ y` est la notation pour le complexe (x + iy)
+
+en: Now compare with the amount of code necessary in C:
+fr: Comparons maintenant avec la quantité de code nécessaire pour le faire en 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);
+
+
+en: For each type, you need to write a new function.
+en: The only way to work around this problem is to use some meta-programming trick, for example using the pre-processor.
+en: In C++ there is a better way, C++ templates:
+fr: Pour chaque type, vous avez besoin d'écrire une nouvelle fonction.
+fr: Le seul moyen de se débarrasser de ce problème est d'utiliser des astuces de méta-programmation, par exemple en utilisant le pré-processeur.
+fr: en C++ il y a un meilleur moyen, les _templates_:
+
+
+#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;
+}
+
+
+en: C++ does a far better job than C in this regard.
+en: But for more complex functions the syntax can be hard to follow:
+en: see [this article](http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/) for example.
+fr: C++ fait un bien meilleur travail que C ici.
+fr: Mais pour des fonctions plus complexes, la syntaxe sera difficile à suivre.
+fr: Voyez [cet article](http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/) pour quelques exemples. (_NDT: toujours en anglais)
+
+en: In C++ you must declare that a function can work with different types.
+en: In Haskell, the opposite is the case.
+en: The function will be as general as possible by default.
+fr: En C++ vous devez déclarer qu'une fonction peut marcher avec différents types.
+fr: En Haskell, c'est le contraire.
+fr: La fonction sera aussi générale que possible par défaut.
+
+en: Type inference gives Haskell the feeling of freedom that dynamically typed languages provide.
+en: But unlike dynamically typed languages, most errors are caught before run time.
+en: Generally, in Haskell:
+fr: L'inférence de type donne à Haskell le sentiment de liberté que les langages dynamiquement typés proposent.
+fr: Mais contrairement aux langages dynamiquement typés, la majorité des erreurs est détectée avant de lancer le programme.
+fr: Généralement, en Haskell:
+
+en: > "if it compiles it certainly does what you intended"
+fr: > "Si ça compile, ça fait certainement ce que vous attendiez."
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs
new file mode 100644
index 0000000..8bfb530
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs
@@ -0,0 +1,19 @@
+en:
Type construction
+fr:
Construction de types
+
+en: You can construct your own types.
+en: First, you can use aliases or type synonyms.
+fr: Vous pouvez construire vos propres types.
+fr: D'abord, vous pouvez utiliser des alias ou des synonymes de types.
+
+> 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
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs
new file mode 100644
index 0000000..e41679b
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs
@@ -0,0 +1,90 @@
+
+en: But it doesn't protect you much.
+en: Try to swap the two parameter of `showInfos` and run the program:
+fr: Mais cela ne vous protège pas tellement.
+fr: Essayez d'inverser les deux paramètres de `showInfos` et lancez le programme:
+
+
+ putStrLn $ showInfos color name
+
+
+en: It will compile and execute.
+en: In fact you can replace Name, Color and String everywhere.
+en: The compiler will treat them as completely identical.
+fr: Le code sera compilé et exécuté.
+fr: En fait vous pouvez remplace Name, Color et String n'importe où.
+fr: Le compilateur les traitera comme si ils était complétement identiques.
+
+en: Another method is to create your own types using the keyword `data`.
+fr: Une autre méthode est de créer vos propres type avec le mot-clé `data`.
+
+> 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
+
+en: Now if you switch parameters of `showInfos`, the compiler complains!
+en: So this is a potential mistake you will never make again and the only price is to be more verbose.
+fr: Maintenant, si vous échangez les paramètres de `showInfos`, le compilateur se plaint!
+fr: Au seul prix d'être plus verbeux, vous écartez définitivement cette erreur potentielle.
+
+en: Also notice that constructors are functions:
+fr: Remarquez aussi que les constructeurs sont des fonctions :
+
+
+NameConstr :: String -> Name
+ColorConstr :: String -> Color
+
+
+en: The syntax of `data` is mainly:
+fr: La syntaxe de `data` est principalement:
+
+
+data TypeName = ConstructorName [types]
+ | ConstructorName2 [types]
+ | ...
+
+
+en: Generally the usage is to use the same name for the
+en: DataTypeName and DataTypeConstructor.
+fr: Généralement on utilise le même nom pour le DatatTypeName et le DataTypeConstructor.
+
+en: Example:
+fr: Exemple :
+
+
+data Complex a = Num a => Complex a a
+
+
+en: Also you can use the record syntax:
+fr: Vous pouvez également utiliser cette syntaxe :
+
+
+data DataTypeName = DataConstructor {
+ field1 :: [type of field1]
+ , field2 :: [type of field2]
+ ...
+ , fieldn :: [type of fieldn] }
+
+
+en: And many accessors are made for you.
+en: Furthermore you can use another order when setting values.
+fr: Et de nombreux accesseurs sont définis pour vous.
+fr: En outre, vous pouvez utiliser une autre ordre lorsque vous définissez des valeurs.
+
+en: Example:
+fr: Exemple :
+
+
+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
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs
new file mode 100644
index 0000000..38de0c0
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs
@@ -0,0 +1,50 @@
+en:
Recursive type
+fr:
Type récursif
+
+en: You already encountered a recursive type: lists.
+en: You can re-create lists, but with a more verbose syntax:
+fr: Nous avons déjà rencontré un type récursif : les listes.
+fr: Nous pourrions re-créer les listes, avec une syntaxe plus bavarde:
+
+
+data List a = Empty | Cons a (List a)
+
+
+
+en: If you really want to use an easier syntax you can use an infix name for constructors.
+fr: Si vous voulez réellement utiliser une syntxe plus simple, utilisez un nom infixe pour les constructeurs.
+
+
+infixr 5 :::
+data List a = Nil | a ::: (List a)
+
+
+en: The number after `infixr` gives the precedence.
+fr: Le nombre après `infixr` donne la priorité.
+
+en: 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.
+fr: Si vous voulez pouvoir écrire (`Show`), lire (`Read`), tester l'égalité (`Eq`) et comparer (`Ord`) votre nouvelle structure, vous pouvez demander à Haskell de dériver les fonctions appropriées pour vous.
+
+> infixr 5 :::
+> data List a = Nil | a ::: (List a)
+> deriving (Show,Read,Eq,Ord)
+
+en: When you add `deriving (Show)` to your data declaration, Haskell creates a `show` function for you.
+en: We'll see soon how you can use your own `show` function.
+fr: Quand vous ajoutez `deriving (Show)` à votre déclaration, Haskell crée une fonction `show` pour vous.
+fr: Nous verrons bientôt comment utiliser sa propre fonction `show`.
+
+> convertList [] = Nil
+> convertList (x:xs) = x ::: convertList xs
+
+> main = do
+> print (0 ::: 1 ::: Nil)
+> print (convertList [0,1])
+
+en: This prints:
+fr: Ceci donne :
+
+~~~
+0 ::: (1 ::: Nil)
+0 ::: (1 ::: Nil)
+~~~
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs
new file mode 100644
index 0000000..3441406
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs
@@ -0,0 +1,49 @@
+en:
Trees
+fr:
Les arbres
+
+blogimage("magritte-l-arbre.jpg","Magritte, l'Arbre")
+
+en: We'll just give another standard example: binary trees.
+fr: Voici une autre exemple standard : les arbres binaires.
+
+> import Data.List
+>
+> data BinTree a = Empty
+> | Node a (BinTree a) (BinTree a)
+> deriving (Show)
+
+en: We will also create a function which turns a list into an ordered binary tree.
+fr: Créons aussi une fonctions qui transforme une liste en un arbre binaire ordonné.
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+en: Look at how elegant this function is.
+en: In plain English:
+fr: Remarquez à quel point cette fonction est élégante.
+fr: En français :
+
+en: - an empty list will be converted to an empty tree.
+en: - a list `(x:xs)` will be converted to a tree where:
+en: - The root is `x`
+en: - Its left subtree is the tree created from members of the list `xs` which are strictly inferior to `x` and
+en: - the right subtree is the tree created from members of the list `xs` which are strictly superior to `x`.
+fr: - une liste vide est convertie en un arbre vide
+fr: - une liste `(x:xs)` sera convertie en un arbre où :
+fr: - La racine est `x`
+fr: - Le "sous-arbre" de gauche est l'arbre créé à partir des membres de la liste `xs` strictement inférieurs à `x`
+fr: - Le "sous-arbre" de droite est l'arbre créé à partir des membres de la liste `xs` strictement superieurs à `x`
+
+> main = print $ treeFromList [7,2,4,8]
+
+en: You should obtain the following:
+fr: Vous devriez obtenir :
+
+~~~
+Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)
+~~~
+
+en: This is an informative but quite unpleasant representation of our tree.
+fr: C'est une représentation de notre arbre informative mais plutôt déplaisante.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs
new file mode 100644
index 0000000..7a90d6a
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs
@@ -0,0 +1,231 @@
+en: Just for fun, let's code a better display for our trees.
+en: I simply had fun making a nice function to display trees in a general way.
+en: You can safely skip this part if you find it too difficult to follow.
+fr: Juste pour le plaisir, codons un meilleur affichage pour nos arbres.
+fr: Je me suis simplement amusé à faire une belle fonction pour afficher les arbres de façon générale.
+fr: Vous pouvez passer cette partie si vous la trouvez difficile à suivre.
+
+en: We have a few changes to make.
+en: We remove the `deriving (Show)` from the declaration of our `BinTree` type.
+en: 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.
+fr: Nous avons quelques changements à faire.
+fr: Enlevons le `deriving (Show)` de la déclaration de notre type `BinTree`.
+fr: Il serait aussi utile de faire de BinTree une instance de (`Eq` et `Ord`), nous serons ainsi capable de tester l'égalité et de comparer des arbres.
+
+> data BinTree a = Empty
+> | Node a (BinTree a) (BinTree a)
+> deriving (Eq,Ord)
+
+en: Without the `deriving (Show)`, Haskell doesn't create a `show` method for us.
+en: We will create our own version of `show`.
+en: To achieve this, we must declare that our newly created type `BinTree a`
+en: is an instance of the type class `Show`.
+en: The general syntax is:
+fr: Sans le `deriving (Show)`, Haskell ne crée pas de méthode `show` pour nous.
+fr: Nous allons créer notre propre version.
+fr: Pour accomplir cela, nous devons déclarer que notre type `BinTree a`
+fr: est une instance de la classe de type `Show`.
+fr: La syntaxe générale est :
+
+
+instance Show (BinTree a) where
+en: show t = ... -- You declare your function here
+fr: show t = ... -- Déclarez votre fonction ici
+
+
+en: Here is my version of how to show a binary tree.
+en: Don't worry about the apparent complexity.
+en: I made a lot of improvements in order to display even stranger objects.
+fr: Voici ma version pour afficher un arbre binaire.
+fr: Ne vous inquiétez pas de sa complexité apparente.
+fr: J'ai fait beaucoup d'améliorations pour afficher même les objets les plus étranges.
+
+> -- 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"
+
+
+en: The `treeFromList` method remains identical.
+fr: La méthode `treeFromList` reste identique.
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+en: And now, we can play:
+fr: Et maintenant, nous pouvons jouer :
+
+> main = do
+> putStrLn "Int binary tree:"
+> print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
+
+~~~
+en: Int binary tree:
+fr: Arbre binaire d'Int:
+< 7
+: |--2
+: | |--1
+: | `--4
+: | |--3
+: | `--6
+: `--8
+: `--21
+: |--12
+: `--23
+~~~
+
+en: Now it is far better!
+en: The root is shown by starting the line with the `<` character.
+en: And each following line starts with a `:`.
+en: But we could also use another type.
+fr: Maintenant c'est beaucoup mieux !
+fr: La racine est montrée en commençant la ligne avec le caractère `<`.
+fr: Et chaque ligne suivante est commence par `:`.
+fr: Mais nous pourrions aussi utiliser un autre type.
+
+> putStrLn "\nString binary tree:"
+> print $ treeFromList ["foo","bar","baz","gor","yog"]
+
+~~~
+en: String binary tree:
+fr: Arbre binaire de chaînes de caractères
+< "foo"
+: |--"bar"
+: | `--"baz"
+: `--"gor"
+: `--"yog"
+~~~
+
+en: As we can test equality and order trees, we can
+en: make tree of trees!
+fr: Commme nous pouvons tester l'égalité et ordonner des arbres,
+fr: nous pouvons aussi faire des arbres d'arbres!
+
+> putStrLn "\nBinary tree of Char binary trees:"
+> print ( treeFromList
+> (map treeFromList ["baz","zara","bar"]))
+
+~~~
+en: Binary tree of Char binary trees:
+fr: Arbre binaire d'arbres binaires de Char :
+< < 'b'
+: : |--'a'
+: : `--'z'
+: |--< 'b'
+: | : |--'a'
+: | : `--'r'
+: `--< 'z'
+: : `--'a'
+: : `--'r'
+~~~
+
+en: This is why I chose to prefix each line of tree display by `:` (except for the root).
+fr: C'est pour cela que j'ai choisi de préfixer chaque ligne par un `:` (sauf pour la racine).
+
+blogimage("yo_dawg_tree.jpg","Yo Dawg Tree")
+
+> putStrLn "\nTree of Binary trees of Char binary trees:"
+> print $ (treeFromList . map (treeFromList . map treeFromList))
+> [ ["YO","DAWG"]
+> , ["I","HEARD"]
+> , ["I","HEARD"]
+> , ["YOU","LIKE","TREES"] ]
+
+en: Which is equivalent to
+fr: Qui est équivalent à
+
+
+print ( treeFromList (
+ map treeFromList
+ [ map treeFromList ["YO","DAWG"]
+ , map treeFromList ["I","HEARD"]
+ , map treeFromList ["I","HEARD"]
+ , map treeFromList ["YOU","LIKE","TREES"] ]))
+
+
+en: and gives:
+fr: et donne :
+
+~~~
+en: Binary tree of Binary trees of Char binary trees:
+fr: Arbre d'arbres d'arbres de Char :
+< < < 'Y'
+: : : `--'O'
+: : `--< 'D'
+: : : |--'A'
+: : : `--'W'
+: : : `--'G'
+: |--< < 'I'
+: | : `--< 'H'
+: | : : |--'E'
+: | : : | `--'A'
+: | : : | `--'D'
+: | : : `--'R'
+: `--< < 'Y'
+: : : `--'O'
+: : : `--'U'
+: : `--< 'L'
+: : : `--'I'
+: : : |--'E'
+: : : `--'K'
+: : `--< 'T'
+: : : `--'R'
+: : : |--'E'
+: : : `--'S'
+~~~
+
+en: Notice how duplicate trees aren't inserted;
+en: there is only one tree corresponding to `"I","HEARD"`.
+en: We have this for (almost) free, because we have declared Tree to be an instance of `Eq`.
+fr: Remarquez que les arbres en double ne sont pas insérés.
+fr: Il n'y a qu'un seul arbre correspondant à `"I","HEARD"`.
+fr: Nous avons ceci presque gratuitement, car nous avons déclaré Tree comme instance de `Eq`.
+
+en: See how awesome this structure is:
+en: We can make trees containing not only integers, strings and chars, but also other trees.
+en: And we can even make a tree containing a tree of trees!
+fr: Voyez à quel point cette structure est formidable :
+fr: Nous pouvons faire des arbres contenant seulement des entiers, des chaînes de caractères, mais aussi d'autres arbres.
+fr: Et nous pouvons même faire un arbre contenant un arbre d'arbres!
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs
new file mode 100644
index 0000000..e8bf462
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs
@@ -0,0 +1,59 @@
+en:
Infinite Structures
+fr:
Structures infinies
+
+blogimage("escher_infinite_lizards.jpg","Escher")
+
+en: It is often said that Haskell is _lazy_.
+fr: On dit souvent que Haskell est _paresseux_.
+
+en: In fact, if you are a bit pedantic, you should say that [Haskell is _non-strict_](http://www.haskell.org/haskellwiki/Lazy_vs._non-strict).
+en: Laziness is just a common implementation for non-strict languages.
+fr: En fait, si vous êtes un petit peu pédant, vous devriez dire que [Haskell est _non-strict_](http://www.haskell.org/haskellwiki/Lazy_vs._non-strict) (_NDT: En anglais, pour changer_).
+fr: La paresse est juste une implémentation commune aux langages non-stricts.
+
+en: Then what does "not-strict" mean? From the Haskell wiki:
+fr: Alors que signifie "non-strict"? D'après le wiki de Haskell :
+
+en: > Reduction (the mathematical term for evaluation) proceeds from the outside in.
+en: >
+en: > so if you have `(a+(b*c))` then you first reduce `+` first, then you reduce the inner `(b*c)`
+fr: > La réduction (terme mathématique pour "évaluation") procède depuis l'extérieur.
+fr: >
+fr: > Donc si vous avez `(a+(b*c))`, alors vous réduisez `+` d'abord, puis vous réduisez `(b*c)`
+
+en: For example in Haskell you can do:
+fr: Par exemple en Haskell vous pouvez faire :
+
+> -- 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
+
+en: And it stops.
+fr: Et ça s'arrête.
+
+en: How?
+fr: Comment ?
+
+en: Instead of trying to evaluate `numbers` entirely,
+en: it evaluates elements only when needed.
+fr: Au lieu d'essayer d'évaluer `numbers` entièrement,
+fr: Haskell évalue les éléments seulement lorsque c'est nécessaire.
+
+en: Also, note in Haskell there is a notation for infinite lists
+fr: Remarquez aussi qu'en Haskell, il y a une notation pour les listes infinies
+
+~~~
+[1..] ⇔ [1,2,3,4...]
+[1,3..] ⇔ [1,3,5,7,9,11...]
+~~~
+
+en: and most functions will work with them.
+en: Also, there is a built-in function `take` which is equivalent to our `take'`.
+fr: et que la majorité des fonctions fonctionnera avec ces listes.
+fr: Il y a aussi une fonction `take` équivalente à notre `take'`.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs
new file mode 100644
index 0000000..f8384ff
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs
@@ -0,0 +1,173 @@
+
+
+
+This code is mostly the same as the previous one.
+
+> import Debug.Trace (trace)
+> 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"
+>
+
+
+
+en: Suppose we don't mind having an ordered binary tree.
+en: Here is an infinite binary tree:
+fr: Supposons que nous ne nous préoccupions pas d'avoir une arbre ordonné.
+fr: Voici un arbre binaire infini :
+
+> nullTree = Node 0 nullTree nullTree
+
+en: A complete binary tree where each node is equal to 0.
+en: Now I will prove you can manipulate this object using the following function:
+fr: Un arbre complet où chaque noeud est égal à 0.
+fr: Maintenant je vais vous prouver que nous pouvons manipuler cet arbre avec la fonction suivante :
+
+> -- 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
+
+en: See what occurs for this program:
+fr: Regardez ce qui se passe avec ce programme :
+
+
+main = print $ treeTakeDepth 4 nullTree
+
+
+en: This code compiles, runs and stops giving the following result:
+fr: Le code compile, se lance et s'arrête en donnant ce résultat :
+
+~~~
+< 0
+: |-- 0
+: | |-- 0
+: | | |-- 0
+: | | `-- 0
+: | `-- 0
+: | |-- 0
+: | `-- 0
+: `-- 0
+: |-- 0
+: | |-- 0
+: | `-- 0
+: `-- 0
+: |-- 0
+: `-- 0
+~~~
+
+en: Just to heat up your neurones a bit more,
+en: let's make a slightly more interesting tree:
+fr: Pour nous chauffer encore un peu les neurones,
+fr: faisons un arbre plus intéressant :
+
+> 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)
+
+en: Another way to create this tree is to use a higher order function.
+en: This function should be similar to `map`, but should work on `BinTree` instead of list.
+en: Here is such a function:
+fr: Un autre moyen de créer cet arbre est d'utiliser une fonction d'ordre supérieur.
+fr: Cette fonction devrait être similaire à `map` n, mais devrait travailler sur un `BinTree` au lieu d'une liste.
+fr: Voici cette fonction :
+
+> -- 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)
+
+en: _Hint_: I won't talk more about this here.
+en: If you are interested in the generalization of `map` to other data structures,
+en: search for functor and `fmap`.
+fr: _NB_: Je ne parlerai pas plus de cette fonction ici.
+fr: Si vous vous intéressez à la généralisation de `map` à d'autres structures de données,
+fr: cherchez des informations sur les foncteurs et `fmap`.
+
+en: Our definition is now:
+fr: Notre définition est maintenant :
+
+> infTreeTwo :: BinTree Int
+> infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
+> (treeMap (\x -> x+1) infTreeTwo)
+
+en: Look at the result for
+fr: Regardez le résultat pour
+
+
+main = print $ treeTakeDepth 4 infTreeTwo
+
+
+~~~
+< 0
+: |-- -1
+: | |-- -2
+: | | |-- -3
+: | | `-- -1
+: | `-- 0
+: | |-- -1
+: | `-- 1
+: `-- 1
+: |-- 0
+: | |-- -1
+: | `-- 1
+: `-- 2
+: |-- 1
+: `-- 3
+~~~
+
+
+
+
+> main = do
+> print $ treeTakeDepth 4 nullTree
+> print $ treeTakeDepth 4 infTreeTwo
+
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs
new file mode 100644
index 0000000..7186e7a
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs
@@ -0,0 +1,28 @@
+en:
Hell Difficulty Part
+fr:
Partie de difficulté infernale
+
+en: Congratulations for getting so far!
+en: Now, some of the really hardcore stuff can start.
+fr: Félicitations pour être allé si loin!
+fr: Maitenant, les choses vraiment extrêmes peuvent commencer.
+
+en: If you are like me, you should get the functional style.
+en: You should also understand a bit more the advantages of laziness by default.
+en: But you also don't really understand where to start in order to make a real
+en: program.
+en: And in particular:
+fr: Si vous êtes comme moi, vous êtes déjà familier avec le style fonctionnel.
+fr: Vous devriez également comprendre les avantages de la paresse par défaut.
+fr: Mais vous ne comprenez peut-être pas vraiment par où commencer pour faire un vrai
+fr: programme.
+fr: Et en particulier :
+
+en: - How do you deal with effects?
+en: - Why is there a strange imperative-like notation for dealing with IO?
+fr: - Comment s'occuper des effets ?
+fr: - Pourquoi y a t-il une étrange notation impérative lorsque l'on s'occupe de l'Entrée/Sortie? (E/S, _IO_ pour _Input/Output_ en anglais)
+
+en: Be prepared, the answers might be complex.
+en: But they are all very rewarding.
+fr: Accrochez-vous, les réponses risquent d'être compliquées.
+fr: Mais elles en valent la peine.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs
new file mode 100644
index 0000000..5d64772
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs
@@ -0,0 +1,117 @@
+en:
Deal With IO
+fr:
S'occuper de l'E/S (IO)
+
+blogimage("magritte_carte_blanche.jpg","Magritte, Carte blanche")
+
+ > %tldr
+ >
+en: > A typical function doing `IO` looks a lot like an imperative program:
+fr: > Une fonction typique qui fait de l'`IO` ressemble à un programme impératif:
+ >
+ > ~~~
+ > f :: IO a
+ > f = do
+ > x <- action1
+ > action2 x
+ > y <- action3
+ > action4 x y
+ > ~~~
+ >
+en: > - To set a value to an object we use `<-` .
+en: > - The type of each line is `IO *`;
+en: > in this example:
+fr: > - Pour définir la valeur d'un objet on utilise `<-` .
+fr: > - Le type de chaque ligne est `IO *`;
+fr: > dans cet exemple:
+ > - `action1 :: IO b`
+ > - `action2 x :: IO ()`
+ > - `action3 :: IO c`
+ > - `action4 x y :: IO a`
+ > - `x :: b`, `y :: c`
+en: > - Few objects have the type `IO a`, this should help you choose.
+en: > In particular you cannot use pure functions directly here.
+en: > To use pure functions you could do `action2 (purefunction x)` for example.
+fr: > - Quelques objets ont le type `IO a`, cela devrait vous aider à choisir.
+fr: > En particulier vous ne pouvez pas utiliser de fonctions pures directement ici.
+fr: > Pour utiliser des fonctions pures vous pourriez faire `action2 (pureFunction x)` par exemple.
+
+en: In this section, I will explain how to use IO, not how it works.
+en: You'll see how Haskell separates the pure from the impure parts of the program.
+fr: Dans cette section, je vais expliquer comment utiliser l'IO, pas comment ça marche.
+fr: Vous verrez comment Haskell sépare les parties pures et impures du programme.
+
+en: Don't stop because you're trying to understand the details of the syntax.
+en: Answers will come in the next section.
+fr: Ne vous arrêtez pas sur les détails de la syntaxe
+fr: Les réponses viendront dans la section suivante.
+
+en: What to achieve?
+fr: Que cherchons-nous à faire?
+
+en: > Ask a user to enter a list of numbers.
+en: > Print the sum of the numbers
+fr: > Demander une liste de nombres à l'utilisateur.
+fr: > Afficher la somme de ces nombres.
+
+> toList :: String -> [Integer]
+> toList input = read ("[" ++ input ++ "]")
+>
+> main = do
+> putStrLn "Enter a list of numbers (separated by comma):"
+> input <- getLine
+> print $ sum (toList input)
+
+en: It should be straightforward to understand the behavior of this program.
+en: Let's analyze the types in more detail.
+fr: Il devrait être simple de comprendre le comportement de ce programme.
+fr: Analysons les types en détails.
+
+~~~
+putStrLn :: String -> IO ()
+getLine :: IO String
+print :: Show a => a -> IO ()
+~~~
+
+en: Or more interestingly, we note that each expression in the `do` block has a type of `IO a`.
+fr: Ou, de manièree plus intéressante, on remarque que chaque expression dans le bloc `do` est de type `IO a`.
+
+
+
+en: We should also pay attention to the effect of the `<-` symbol.
+fr: Nous devrions aussi prêter attention à l'effet du symbole `<-`.
+
+~~~
+do
+ x <- something
+~~~
+
+en: If `something :: IO a` then `x :: a`.
+fr: Si `something :: IO a` alors `x :: a`.
+
+en: Another important note about using `IO`:
+en: All lines in a do block must be of one of the two forms:
+fr: Une autre remarque importante sur l'`IO`:
+fr: Toutes les lignes d'un bloc `do` doivent être d'une des deux formes suivantes :
+
+~~~
+action1 :: IO a
+ -- in this case, generally a = ()
+~~~
+
+ou
+
+~~~
+value <- action2 -- where
+ -- action2 :: IO b
+ -- value :: b
+~~~
+
+en: These two kinds of line will correspond to two different ways of sequencing actions.
+en: The meaning of this sentence should be clearer by the end of the next section.
+fr: Ces deux types de ligne correspondent à deux types différents de séquençage d'action.
+fr: La signification de cette phrase devrait être plus claire à la fin de la prochaine section.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs
new file mode 100644
index 0000000..4feec04
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs
@@ -0,0 +1,123 @@
+en: Now let's see how this program behaves.
+en: For example, what happens if the user enters something strange?
+en: Let's try:
+fr: Maintenant voyons comment ce programme se comporte.
+fr: Par exemple, que ce passe-t-il si l'utilisateur entre une mauvaise valeur?
+fr: Essayons :
+
+~~~
+ % runghc 02_progressive_io_example.lhs
+ Enter a list of numbers (separated by comma):
+ foo
+ Prelude.read: no parse
+~~~
+
+
+en: Argh! An evil error message and a crash!
+fr: Argh! Un message d'erreur effrayant et un crash !
+en: Our first improvement will simply be to answer with a more friendly message.
+fr: Notre première amélioration sera de répondre avec un message plus amical.
+
+en: In order to do this, we must detect that something went wrong.
+fr: Pour faire cela, nous devons détecter que quelque chose s'est mal passé.
+en: Here is one way to do this: use the type `Maybe`.
+fr: Voici un moyen de le faire : utiliser le type `Maybe`.
+en: This is a very common type in Haskell.
+fr: C'est un type très utilisé en Haskell.
+
+> import Data.Maybe
+
+en: What is this thing? `Maybe` is a type which takes one parameter.
+fr: Mais qu'est-ce que c'est ? `Maybe` est un type qui prend un paramètre.
+en: Its definition is:
+fr: Sa définition est :
+
+
+data Maybe a = Nothing | Just a
+
+
+fr: C'est un bon moyen de dire qu'il y a eu une erreur en essayant de créer/évaluer
+en: This is a nice way to tell there was an error while trying to create/compute
+fr: une valeur.
+en: a value.
+fr: La fonction `maybeRead` en est un bon exemple.
+en: The `maybeRead` function is a great example of this.
+fr: C'est une fonction similaire à `read`[^1],
+en: This is a function similar to the function `read`[^1],
+fr: mais s'il y a un problème, la valeur retournée est `Nothing`.
+en: but if something goes wrong the returned value is `Nothing`.
+fr: Si la valeur est bonne, la valeur retournée est `Just `.
+en: If the value is right, it returns `Just `.
+fr: Ne vous efforcez pas trop de comprendre cette fonction.
+en: Don't try to understand too much of this function.
+fr: J'utilise une fonction de plus bas niveau que `read` : `reads`.
+en: I use a lower level function than `read`: `reads`.
+
+fr: [^1]: Qui est elle-même très similaire à la fonction `eval` de javascript, appliquée sur une chaîne contenant du code au format JSON.
+en: [^1]: Which is itself very similar to the javascript `eval` function, that is applied to a string containing JSON.
+
+> maybeRead :: Read a => String -> Maybe a
+> maybeRead s = case reads s of
+> [(x,"")] -> Just x
+> _ -> Nothing
+
+fr: Maintenant, pour être plus lisible, on définit une fonction comme ceci :
+en: Now to be a bit more readable, we define a function which goes like this:
+fr: Si la chaîne a un mauvais format, elle retournera `Nothing`.
+en: If the string has the wrong format, it will return `Nothing`.
+fr: Sinon, par exemple pour "1,2,3", cela retournera `Just [1,2,3]`.
+en: Otherwise, for example for "1,2,3", it will return `Just [1,2,3]`.
+
+> getListFromString :: String -> Maybe [Integer]
+> getListFromString str = maybeRead $ "[" ++ str ++ "]"
+
+
+en: We simply have to test the value in our main function.
+fr: Nous avons juste à tester la valeur dans notre fonction principale.
+
+> 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."
+
+fr: En cas d'erreur, on affiche un joli message.
+en: In case of error, we display a nice error message.
+
+fr: Notez que le type de chaque expression dans le bloc `do` de `main` reste de la forme `IO a`.
+en: Note that the type of each expression in the main's `do` block remains of the form `IO a`.
+fr: La seule construction étrange est `error`.
+en: The only strange construction is `error`.
+fr: Disons juste que `error msg` prend le type nécessaire (ici, `IO ()`).
+en: I'll just say here that `error msg` takes the needed type (here `IO ()`).
+
+fr: Une chose très importante à noter est le type de toutes les fonctions définies jusqu'ici.
+en: One very important thing to note is the type of all the functions defined so far.
+fr: Il n'y a qu'une seule fonction qui contient `IO` dans son type : `main`.
+en: There is only one function which contains `IO` in its type: `main`.
+fr: Cela signifie que `main` est impure.
+en: This means main is impure.
+fr: Mais `main` utilise `getListFromString`, qui, elle, est pure.
+en: But main uses `getListFromString` which is pure.
+fr: Nous pouvons donc facilement repérer quelles fonctions sont pures
+en: So it's clear just by looking at declared types which functions are pure and
+fr: et lesquelles sont impures, seulement en regardant leur type.
+en: which are impure.
+
+fr: Pourquoi la pureté a-t-elle de l'importance?
+en: Why does purity matter?
+fr: Parmi ses nombreux avantages, en voici trois :
+en: Among the many advantages, here are three:
+
+fr: - Il est beaucoup plus facile de penser à du code pur qu'à du code impur.
+en: - It is far easier to think about pure code than impure code.
+fr: - La pureté vous protège de tous les bugs difficiles à reproduire dûs aux [effets de bord](https://fr.wikipedia.org/wiki/Effet_de_bord_(informatique)).
+en: - Purity protects you from all the hard-to-reproduce bugs that are due to side effects.
+fr: - Vous pouvez évaluer des fonctions pures dans n'importe quel ordre ou en parallèle, sans prendre de risques.
+en: - You can evaluate pure functions in any order or in parallel without risk.
+
+fr: C'est pourquoi vous devriez mettre le plus de code possible dans des fonctions pures.
+en: This is why you should generally put as most code as possible inside pure functions.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs
new file mode 100644
index 0000000..8293a83
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs
@@ -0,0 +1,80 @@
+fr: La prochaine étape sera de demander la liste de nombres à l'utilisateur encore et encore jusqu'à ce qu'il entre une réponse valide.
+en: Our next iteration will be to prompt the user again and again until she enters a valid answer.
+
+fr: Nous gardons la première partie :
+en: We keep the first part:
+
+> 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 ++ "]"
+
+fr: Maintenant nous créons la fonction qui demandera une liste d'entiers à l'utilisateur
+en: Now we create a function which will ask the user for an list of integers
+fr: jusqu'à ce que l'entrée soit correcte
+en: until the input is right.
+
+> 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
+
+fr: Cette fonction est de type `IO [Integer]`.
+en: This function is of type `IO [Integer]`.
+fr: Cela signifie que la valeur récupérée est de type `[Integer`] et est le résultat d'actions d'E/S.
+en: Such a type means that we retrieved a value of type `[Integer]` through some IO actions.
+fr: D'aucuns diront avec enthousiasme :
+en: Some people might explain while waving their hands:
+
+fr: > «C'est un `[Integer]` dans un `IO` !»
+en: > «This is an `[Integer]` inside an `IO`»
+
+fr: Si vous voulez comprendre les détails derrière tout cela, vous devrez lire la prochaine section.
+en: If you want to understand the details behind all of this, you'll have to read the next section.
+fr: Mais si vous voulez seulement _utiliser_ l'E/S, contentez-vous pratiquer un peu et rappelez-vous de penser aux types.
+en: But really, if you just want to _use_ IO just practice a little and remember to think about the type.
+
+fr: Finalement, notre fonction `main`est bien plus simple :
+en: Finally our main function is much simpler:
+
+> main :: IO ()
+> main = do
+> list <- askUser
+> print $ sum list
+
+fr: Nous avons fini notre introduction à l'`IO`.
+en: We have finished with our introduction to `IO`.
+fr: C'était plutôt rapide. Voici les principales choses à retenir :
+en: This was quite fast. Here are the main things to remember:
+
+fr: - Dans le bloc `do`, chaque expression doit avoir le type `IO a`.
+en: - in the `do` block, each expression must have the type `IO a`.
+fr: Vous êtes donc limité quant au panel d'expression disponibles.
+en: You are then limited with regard to the range of expressions available.
+fr: Par exemple, `getLine`, `print`, `putStrLn`, etc...
+en: For example, `getLine`, `print`, `putStrLn`, etc...
+fr: - Essayez d'externaliser le plus possible les fonctions pures.
+en: - Try to externalize the pure functions as much as possible.
+fr: - le type `IO a` signifie : une _action_ d'E/S qui retourne un élément de type a.
+en: - the `IO a` type means: an IO _action_ which returns an element of type `a`.
+fr: L'`IO` représente des actions; sous le capot, `IO a` est le type d'une fonction.
+en: `IO` represents actions; under the hood, `IO a` is the type of a function.
+fr: Lisez la prochaine section si vous êtes curieux.
+en: Read the next section if you are curious.
+
+fr: Si vous pratiquez un peu, vous devriez être capable d'_utiliser_ l'`IO`.
+en: If you practice a bit, you should be able to _use_ `IO`.
+
+fr: > -Exercices_:
+en: > _Exercises_:
+ >
+fr: > - Écrivez un programme qui additionne tous ses arguments. Utilisez la fonction `getArgs`.
+en: > - Make a program that sums all of its arguments. Hint: use the function `getArgs`.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs
new file mode 100644
index 0000000..6cfd73f
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs
@@ -0,0 +1,549 @@
+fr:
Le truc des IO révélé
+en:
IO trick explained
+
+blogimage("magritte_pipe.jpg","Magritte, ceci n'est pas une pipe")
+
+fr: > Voici un %tlal pour cette section.
+en: > Here is a %tldr for this section.
+ >
+fr: > Pour séparer les parties pures et impures,
+en: > To separate pure and impure parts,
+fr: > `main` est définie comme une fonction.
+en: > `main` is defined as a function
+fr: > qui modifie l'état du monde.
+en: > which modifies the state of the world.
+ >
+ > ~~~
+ > main :: World -> World
+ > ~~~
+ >
+fr: > Une fonction aura des effets de bord si elle a ce type.
+en: > A function is guaranteed to have side effects only if it has this type.
+fr: > Mais regardez cette fonction `main` typique:
+en: > But look at a typical main function:
+ >
+ > ~~~
+ >
+ > 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
+ > ~~~
+ >
+fr: > Nous avons beaucoup d'élements temporaires (ici, `w1`, `w2` et `w3`)
+en: > We have a lot of temporary elements (here `w1`, `w2` and `w3`)
+fr: > qui doivent être passés à l'action suivante.
+en: > which must be passed on to the next action.
+ >
+fr: > Nous créons une fonction `bind` ou `(>>=)`.
+en: > We create a function `bind` or `(>>=)`.
+fr: > Avec `bind` nous n'avons plus besoin de noms temporaires.
+en: > With `bind` we don't need temporary names anymore.
+ >
+ > ~~~
+ > main =
+ > action1 >>= action2 >>= action3 >>= action4
+ > ~~~
+ >
+fr: > Bonus: Haskell a du sucre syntaxique :
+en: > Bonus: Haskell has syntactical sugar for us:
+ >
+ > ~~~
+ > main = do
+ > v1 <- action1
+ > v2 <- action2 v1
+ > v3 <- action3 v2
+ > action4 v3
+ > ~~~
+
+
+fr: Pourquoi avons-nous utilisé cette syntaxe étrange, et quel est exactement le type `IO`?
+en: Why did we use this strange syntax, and what exactly is this `IO` type?
+fr: Cela peut sembler un peu magique.
+en: It looks a bit like magic.
+
+fr: Pour l'instant, oublions les parties pures de notre programme, et concentrons-nous
+en: For now let's just forget all about the pure parts of our program, and focus
+fr: sur les parties impures:
+en: on the impure parts:
+
+
+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
+
+
+fr: Première remarque : on dirait de l'impératif.
+en: First remark: this looks imperative.
+fr: Haskell est assez puissant pour faire sembler impératif du code impur.
+en: Haskell is powerful enough to make impure code look imperative.
+fr: Par exemple, si vous le vouliez vous pourriez créer une boucle `while` en Haskell.
+en: For example, if you wish you could create a `while` in Haskell.
+fr: En fait, pour utiliser les `IO`, le style impératif est en général plus approprié.
+en: In fact, for dealing with `IO`, an imperative style is generally more appropriate.
+
+fr: Mais vous devriez avoir remarqué que la notation est inhabituelle.
+en: But you should have noticed that the notation is a bit unusual.
+fr: Voici pourquoi, en détail.
+en: Here is why, in detail.
+
+fr: Dans un langage impur, l'état du monde peut être vu comme une énorme variable globale cachée.
+en: In an impure language, the state of the world can be seen as a huge hidden global variable.
+fr: Cette variable cachée est accessible par toutes les fonctions du langage.
+en: This hidden variable is accessible by all functions of your language.
+fr: Par exemple, vous pouvez lire et écrire dans un fichier avec n'importe quelle fonction.
+en: For example, you can read and write a file in any function.
+fr: Le fait que le fichier putatif existe ou non est une éventualité qui relève des états possibles que le monde courant peut prendre.
+en: Whether a file exists or not is a difference in the possible states that the world can take.
+
+fr: En Haskell l'état courant du monde n'est pas caché.
+en: In Haskell the current state of the world is not hidden.
+fr: Au contraire, il est dit _explicitement_ que `main` est une fonction qui change _potentiellement_ l'état du monde.
+en: Rather, it is _explicitly_ said that `main` is a function that _potentially_ changes the state of the world.
+fr: Son type est donc quelque chose comme :
+en: Its type is then something like:
+
+
+main :: World -> World
+
+
+fr: Les fonctions ne sont pas toutes susceptibles de modifier cette variable.
+en: Not all functions may access this variable.
+fr: Celle qui peuvent la modifier sont impures.
+en: Those which have access to this variable are impure.
+fr: Les fonctions qui ne peuvent pas agir sur la variable sont pures[^032001].
+en: Functions to which the world variable isn't provided are pure[^032001].
+
+fr: [^032001]: Il y a quelques exceptions _peu sûres_ à cette règle. Mais vous ne devriez pas en voir en application réelle, sauf pour le _debugging_.
+en: [^032001]: There are some _unsafe_ exceptions to this rule. But you shouldn't see such use in a real application except maybe for debugging purposes.
+
+fr: Haskell considère l'état du monde comme une variable à passer à `main`.
+en: Haskell considers the state of the world as an input variable to `main`.
+fr: Mais son type réel est plus proche de celui ci[^032002] :
+en: But the real type of main is closer to this one[^032002]:
+
+fr: [^032002]: Pour les curieux, le vrai type est `data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}`. Tous les `#` ont rapport avec l'optimisation et j'ai échangé quelques champs dans mon exemple. Mais c'est l'idée de base.
+en: [^032002]: 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.
+
+
+main :: World -> ((),World)
+
+
+fr: Le type `()` est le type "unit".
+en: The `()` type is the unit type.
+fr: Rien à voir ici.
+en: Nothing to see here.
+
+fr: Maintenant réécrivons notre fonction `main` avec cela à l'esprit :
+en: Now let's rewrite our main function with this in mind:
+
+
+main w0 =
+ let (list,w1) = askUser w0 in
+ let (x,w2) = print (sum list,w1) in
+ x
+
+
+fr: D'abord, on remarque que toutes les fonctions avec des effets de bord doivent avoir le type :
+en: First, we note that all functions which have side effects must have the type:
+
+
+World -> (a,World)
+
+
+fr: où `a` est le type du résultat.
+en: where `a` is the type of the result.
+fr: Par exemple, une fonction `getChar` aura le type `World -> (Char, World).
+en: For example, a `getChar` function should have the type `World -> (Char, World)`.
+
+fr: Une autre chose à noter est l'astuce pour corriger l'ordre d'évaluation.
+en: Another thing to note is the trick to fix the order of evaluation.
+fr: En Haskell, pour évaluer `f a b`, vous avez l'embarras du choix :
+en: In Haskell, in order to evaluate `f a b`, you have many choices:
+
+fr: - évaluer d'abord `a` puis `b` puis `f a b`
+en: - first eval `a` then `b` then `f a b`
+fr: - évaluer d'abord `b` puis `a` puis `f a b`
+en: - first eval `b` then `a` then `f a b`.
+fr: - évaluer `a` et `b` parallèlement, puis `f a b`
+en: - eval `a` and `b` in parallel then `f a b`
+
+fr: Cela vient du fait que nous avons recours à une partie pure du langage.
+en: This is true because we're working in a pure part of the language.
+
+fr: Maintenant, si vous regardez la fonction `main`, vous voyez tout de suite qu'il faut évaluer la première
+en: Now, if you look at the main function, it is clear you must eval the first
+fr: ligne avant la seconde, car pour évaluer la seconde ligne vous devez
+en: line before the second one since to evaluate the second line you have
+fr: utliser un paramètre donné suite à l'évaluation de la première ligne.
+en: to get a parameter given by the evaluation of the first line.
+
+fr: Cette astuce fonctionne très bien.
+en: This trick works like a charm.
+fr: Le compilateur donnera à chaque étape un pointeur sur l'id du nouveau monde courant.
+en: The compiler will at each step provide a pointer to a new real world id.
+fr: En réalité, `print` sera évaluée comme suit :
+en: Under the hood, `print` will evaluate as:
+
+fr: - Écrit quelque chose sur l'écran
+en: - print something on the screen
+fr: - Modifie l'id du monde
+en: - modify the id of the world
+fr: - renvoyer `((), id du nouveau monde)`.
+en: - evaluate as `((),new world id)`.
+
+fr: Maintenant, si jetez un oeil au style de la fonction `main`, vous remarquerez qu'il est clairement peu commode.
+en: Now, if you look at the style of the main function, it is clearly awkward.
+fr: Essayons de faire la même chose avec la fonction `askUser` :
+en: Let's try to do the same to the `askUser` function:
+
+
+askUser :: World -> ([Integer],World)
+
+
+fr: Avant :
+en: Before:
+
+
+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
+
+
+fr: Après :
+en: After:
+
+
+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)
+
+
+fr: C'est similaire, mais peu commode.
+en: This is similar, but awkward.
+fr: Voyez-vous toutes ces variables temporaires `w?`.
+en: Look at all these temporary `w?` names.
+
+fr: Voici la leçon : une implémentation naïve des IO dans les langages fonctionnels purs serait maladroite !
+en: The lesson is: naive IO implementation in Pure functional languages is awkward!
+
+fr: Heureusement, il y a un meilleur moyen de résoudre ce problème.
+en: Fortunately, there is a better way to handle this problem.
+fr: Nous voyons un motif.
+en: We see a pattern.
+fr: Chaque ligne est de la forme :
+en: Each line is of the form:
+
+
+let (y,w') = action x w in
+
+
+fr: Même si pour certaines lignes l'argument `x` n'est pas nécessaire.
+en: Even if for some lines the first `x` argument isn't needed.
+fr: La sortie est un couple, `(answer, newWorldValue)`.
+en: The output type is a couple, `(answer, newWorldValue)`.
+fr: Chaque fonction `f` doit avoir un type similaire à :
+en: Each function `f` must have a type similar to:
+
+
+f :: World -> (a,World)
+
+
+fr: Et ce n'est pas fini, nous pouvons aussi remarquer que nous suivons toujours le même motif :
+en: Not only this, but we can also note that we always follow the same usage pattern:
+
+
+let (y,w1) = action1 w0 in
+let (z,w2) = action2 w1 in
+let (t,w3) = action3 w2 in
+...
+
+
+fr: Chaque action peut prendre de 0 à n paramètres.
+en: Each action can take from 0 to n parameters.
+fr: Et en particulier, chaque action prend comme paramètre le résultat de la ligne précédente.
+en: And in particular, each action can take a parameter from the result of a line above.
+
+fr: Par exemple, nous pourrions aussi avoir :
+en: For example, we could also have:
+
+
+let (_,w1) = action1 x w0 in
+let (z,w2) = action2 w1 in
+let (_,w3) = action3 z w2 in
+...
+
+
+fr: Avec, bien entendu, `actionN w :: (World) -> (a,World)`.
+en: With, of course: `actionN w :: (World) -> (a,World)`.
+
+fr: > IMPORTANT: Il y a seulement 2 schémas importants à considérer :
+en: > IMPORTANT: there are only two important patterns to consider:
+ >
+ > ~~~
+ > let (x,w1) = action1 w0 in
+ > let (y,w2) = action2 x w1 in
+ > ~~~
+ >
+fr: > et
+en: > and
+ >
+ > ~~~
+ > let (_,w1) = action1 w0 in
+ > let (y,w2) = action2 w1 in
+ > ~~~
+
+leftblogimage("jocker_pencil_trick.jpg","Jocker pencil trick")
+
+fr: Maintenant, préparez-vous pour un petit tour de magie !
+en: Now, we will do a magic trick.
+fr: Faisons disparaître les variables temporaires de monde courant.
+en: We will make the temporary world symbols "disappear".
+fr: Nous allons `attacher` (_NDT: `bind` en anglais_) les deux lignes.
+en: We will `bind` the two lines.
+fr: Définissons la fonction `bind`.
+en: Let's define the `bind` function.
+fr: Son type est assez intimidant au début :
+en: Its type is quite intimidating at first:
+
+
+bind :: (World -> (a,World))
+ -> (a -> (World -> (b,World)))
+ -> (World -> (b,World))
+
+
+fr: Mais gardez en tête que `(World -> (a,World))` est le type d'une action d'IO.
+en: But remember that `(World -> (a,World))` is the type for an IO action.
+fr: Renommons-le pour plus de clarté :
+en: Now let's rename it for clarity:
+
+
+type IO a = World -> (a, World)
+
+
+fr: Quelques exemples de fonctions :
+en: Some examples of functions:
+
+
+getLine :: IO String
+print :: Show a => a -> IO ()
+
+
+fr: `getLine` est une action d'E/S qui prend le monde en paramètre et retourne un couple `(String, World)`.
+en: `getLine` is an IO action which takes world as a parameter and returns a couple `(String, World)`.
+fr: Cela peut être résumé par : `getLine` est de type `IO String`, que nous pouvons voir comme une action d'E/S qui retournera une chaîne de caractères "dans une E/S".
+en: 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".
+
+fr: La fonction `print` est elle aussi intéressante.
+en: The function `print` is also interesting.
+fr: Elle prend un argument qui peut être montré avec `show`.
+en: It takes one argument which can be shown.
+fr: En fait, elle prend deux arguments.
+en: In fact it takes two arguments.
+fr: Le premier est la valeur et le deuxième est l'état du monde.
+en: The first is the value to print and the other is the state of world.
+fr: Elle retourne un couple de type `((), World)`.
+en: It then returns a couple of type `((), World)`.
+fr: Cela signifie qu'elle change l'état du monde, mais ne produit pas d'autre donnée.
+en: This means that it changes the state of the world, but doesn't yield any more data.
+
+fr: Ce nouveau type `IO a` nous aide à simplifier le type de `bind` :
+en: This new `IO a` type helps us simplify the type of `bind`:
+
+
+bind :: IO a
+ -> (a -> IO b)
+ -> IO b
+
+
+fr: Cela dit que `bind` prend deux actions d'E/S en paramètres et retourne une autre action d'E/S.
+en: It says that `bind` takes two IO actions as parameters and returns another IO action.
+
+fr: Maintenant, rappelez-vous des motifs _importants_. Le premier était :
+en: Now, remember the _important_ patterns. The first was:
+
+
+pattern1 w0 =
+ let (x,w1) = action1 w0 in
+ let (y,w2) = action2 x w1 in
+ (y,w2)
+
+
+fr: Voyez les types :
+en: Look at the types:
+
+
+action1 :: IO a
+action2 :: a -> IO b
+pattern1 :: IO b
+
+
+fr: Cela ne vous semble-t-il pas familier ?
+en: Doesn't it seem familiar?
+
+
+(bind action1 action2) w0 =
+ let (x, w1) = action1 w0
+ (y, w2) = action2 x w1
+ in (y, w2)
+
+
+fr: L'idée est de cacher l'argument `World` avec cette fonction. Allons-y !
+en: The idea is to hide the World argument with this function. Let's go:
+fr: Par exemple si nous avions voulu simuler :
+en: As an example imagine if we wanted to simulate:
+
+
+let (line1, w1) = getLine w0 in
+let ((), w2) = print line1 in
+((), w2)
+
+
+fr: Maintenant, en utilisant la fonction `bind` :
+en: Now, using the `bind` function:
+
+
+(res, w2) = (bind getLine print) w0
+
+
+fr: Comme `print` est de type `Show a => a -> (World -> ((), World))`, nous savons que `res = ()` (type `unit`)
+en: As print is of type `Show a => a -> (World -> ((), World))`, we know `res = ()` (`unit` type).
+fr: Si vous ne voyez pas ce qui est magique ici, essayons avec trois lignes cette fois.
+en: If you didn't see what was magic here, let's try with three lines this time.
+
+
+
+let (line1,w1) = getLine w0 in
+let (line2,w2) = getLine w1 in
+let ((),w3) = print (line1 ++ line2) in
+((),w3)
+
+
+fr: Qui est équivalent à :
+en: Which is equivalent to:
+
+
+(res,w3) = (bind getLine (\line1 ->
+ (bind getLine (\line2 ->
+ print (line1 ++ line2))))) w0
+
+
+fr: Avez-vous remarqué quelque chose ?
+en: Didn't you notice something?
+fr: Oui, aucune variable `World` temporaire n'est utilisée !
+en: Yes, no temporary World variables are used anywhere!
+fr: C'est _MA_._GIQUE_.
+en: This is _MA_. _GIC_.
+
+fr: Nous pouvons utiliser une meilleure notation.
+en: We can use a better notation.
+fr: Utilisons `(>>=)` au lieu de `bind`.
+en: Let's use `(>>=)` instead of `bind`.
+fr: `(>>=)` est une fonction infixe, comme
+en: `(>>=)` is an infix function like
+fr: `(+)`; pour mémoire : `3 + 4 ⇔ (+) 3 4`
+en: `(+)`; reminder `3 + 4 ⇔ (+) 3 4`
+
+
+(res,w3) = (getLine >>=
+ (\line1 -> getLine >>=
+ (\line2 -> print (line1 ++ line2)))) w0
+
+
+fr: Ho Ho Ho! Joyeux Noël !
+fr; Haskell a confectionné du sucre syntaxique pour vous :
+en: Ho Ho Ho! Merry Christmas Everyone!
+en: Haskell has made syntactical sugar for us:
+
+
+do
+ x <- action1
+ y <- action2
+ z <- action3
+ ...
+
+
+fr: Est remplacé par :
+en: Is replaced by:
+
+
+action1 >>= (\x ->
+action2 >>= (\y ->
+action3 >>= (\z ->
+...
+)))
+
+
+fr: Remarquez que vous pouvez utliser `x` dans `action2` et `x` et `y` dans `action3`.
+en: Note that you can use `x` in `action2` and `x` and `y` in `action3`.
+
+fr: Mais que se passe-t-il pour les lignes qui n'utilisent pas le `<-` ?
+en: But what about the lines not using the `<-`?
+fr: Facile, une autre fonction `blindBind` :
+en: Easy, another function `blindBind`:
+
+
+blindBind :: IO a -> IO b -> IO b
+blindBind action1 action2 w0 =
+ bind action (\_ -> action2) w0
+
+
+fr: Je n'ai pas simplifié cette définition pour plus de clarté.
+en: I didn't simplify this definition for the purposes of clarity.
+fr: Bien sûr, nous pouvons utiliser une meilleure notation avec l'opérateur `(>>)`.
+en: Of course, we can use a better notation: we'll use the `(>>)` operator.
+
+fr: Et
+en: And
+
+
+do
+ action1
+ action2
+ action3
+
+
+fr: Devient
+en: Is transformed into
+
+
+action1 >>
+action2 >>
+action3
+
+
+fr: Enfin, une autre fonction est plutôt utile.
+en: Also, another function is quite useful.
+
+
+putInIO :: a -> IO a
+putInIO x = IO (\w -> (x,w))
+
+
+fr: D'une manière générale, c'est une façon de mettre des valeurs pures dans le "contexte d'E/S".
+en: This is the general way to put pure values inside the "IO context".
+fr: Le nom général pour `putInIO` est `return`.
+en: The general name for `putInIO` is `return`.
+fr: C'est un plutôt un mauvais nom lorsque vous commencez à programmer en Haskell. `return` est très différent de ce à quoi vous pourriez être habitué.
+en: This is quite a bad name when you learn Haskell. `return` is very different from what you might be used to.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs
new file mode 100644
index 0000000..f0c9dee
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs
@@ -0,0 +1,49 @@
+fr: Pour finir, traduisons notre exemple :
+en: To finish, let's translate our example:
+
+
+
+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
+
+
+fr: Se traduit en :
+en: Is translated into:
+
+> 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
+
+fr: Vous pouvez compiler ce code pour vérifier qu'il marche.
+en: You can compile this code to verify that it works.
+
+fr: Imaginez à quoi il ressemblerait sans le `(>>)` et `(>>=)`.
+en: Imagine what it would look like without the `(>>)` and `(>>=)`.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs
new file mode 100644
index 0000000..b658ee8
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs
@@ -0,0 +1,124 @@
+fr:
Les monades
+en:
Monads
+
+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.")
+
+fr: Maintenant le secret peut être dévoilé : `IO` est une _monade_.
+en: Now the secret can be revealed: `IO` is a _monad_.
+fr: Être une monade signifie que vous avez accès à du sucre syntaxique avec la notation `do`.
+en: Being a monad means you have access to some syntactical sugar with the `do` notation.
+fr: Mais principalement, vous avez accès à un motif de codage qui tempérera le flux de votre code.
+en: But mainly, you have access to a coding pattern which will ease the flow of your code.
+
+fr: > **Remarques importantes** :
+en: > **Important remarks**:
+ >
+fr: > - Le monades n'ont pas forcément quoi que ce soit à voir avec les effets de bord !
+en: > - Monad are not necessarily about effects!
+fr: > Il y a beaucoup de monades _pures_.
+en: > There are a lot of _pure_ monads.
+fr: > - Les monades concernent plus le séquençage.
+en: > - Monad are more about sequencing
+
+fr: En Haskell, `Monad` est une classe de type.
+en: In Haskell, `Monad` is a type class.
+fr: Pour être une instance d'une classe de type, vous devez fournir les fonctions `(>>=)` et `return`.
+en: To be an instance of this type class, you must provide the functions `(>>=)` and `return`.
+fr: La fonction `(>>)` est dérivée de `(>>=)`.
+en: The function `(>>)` is derived from `(>>=)`.
+fr: Voici commment la classe de type `Monad` est déclarée (grosso modo) :
+en: Here is how the type class `Monad` is declared (basically):
+
+
+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
+
+fr: -- Vous pouvez ignorer cette fonction généralement,
+en: -- You should generally safely ignore this function
+fr: -- je crois qu'elle existe pour des raisons historiques
+en: -- which I believe exists for historical reasons
+ fail :: String -> m a
+ fail = error
+
+
+
+fr: > Remarques :
+en: > Remarks:
+ >
+fr: > - le mot-clé `class` n'est pas votre ami.
+en: > - the keyword `class` is not your friend.
+fr: > Une classe en Haskell _n'est pas_ du même genre que celle des langages orientés-objet.
+en: > A Haskell class is _not_ a class of the kind you will find in object-oriented programming.
+fr: > Elles ont beaucoup de similarités avec les interfaces de Java.
+en: > A Haskell class has a lot of similarities with Java interfaces.
+fr: > Un meilleur mot aurait été `typeClass`, ce qui signifierait un ensemble de types.
+en: > A better word would have been `typeclass`, since that means a set of types.
+fr: > Pour qu'un type appartienne à une classe, toutes les fonctions de cette classe doivent être fournies pour ce type.
+en: > For a type to belong to a class, all functions of the class must be provided for this type.
+fr: > - Dans cet exemple particulier de classe de type, le type `m` doit être un type qui prend un argument.
+en: > - In this particular example of type class, the type `m` must be a type that takes an argument.
+fr: > par exemple `IO a`, mais aussi `Maybe a`, `[a]`, etc...
+en: > for example `IO a`, but also `Maybe a`, `[a]`, etc...
+fr: > - Pour être une monade utile, votre fonction doit obéir à quelques règles.
+en: > - To be a useful monad, your function must obey some rules.
+fr: > Si votre construction n'obéit pas à ces règles, des choses étranges pourraient se produire :
+en: > If your construction does not obey these rules strange things might happens:
+
+ > ~~~
+ > return a >>= k == k a
+ > m >>= return == m
+ > m >>= (\x -> k x >>= h) == (m >>= k) >>= h
+ > ~~~
+
+fr:
Maybe est une monade
+en:
Maybe is a monad
+
+fr: Il y a beaucoup de types différents qui sont des instances de `Monad`.
+en: There are a lot of different types that are instances of `Monad`.
+fr: L'un des plus faciles à décrire est `Maybe`.
+en: One of the easiest to describe is `Maybe`.
+fr: Si vous avez une séquence de valeurs `Maybe`, vous pouvez utiliser les monades pour les manipuler.
+en: If you have a sequence of `Maybe` values, you can use monads to manipulate them.
+fr: C'est particulièrement utile pour enlever des constructions `if..then..else..` trop nombreuses.
+en: It is particularly useful to remove very deep `if..then..else..` constructions.
+
+fr: Imaginez une opération bancaire complexe. Vous êtes éligible pour gagner 700€ seulement si
+en: Imagine a complex bank operation. You are eligible to gain about 700€ only
+fr: vous pouvez effectuer une liste d'opérations sans tomber en dessous de zéro.
+en: if you can afford to follow a list of operations without your balance dipping below zero.
+
+> 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
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs
new file mode 100644
index 0000000..40efd31
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs
@@ -0,0 +1,23 @@
+fr: Maintenant, améliorons cela en utilisant le fait que `Maybe` est une Monade.
+en: Now, let's make it better using Maybe and the fact that it is a Monad
+
+> 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
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs
new file mode 100644
index 0000000..f0871c5
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs
@@ -0,0 +1,66 @@
+fr: Pas mauvais, mais nous pouvons faire encore mieux :
+en: Not bad, but we can make it even better:
+
+> 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
+
+fr: Nous avons prouvé que les monades sont un bon moyen de rendre notre code plus élégant.
+en: We have proven that Monads are a good way to make our code more elegant.
+fr: Remarquez que cette idée d'organisation de code, en particulier pour `Maybe`, peut être utilisée
+en: Note this idea of code organization, in particular for `Maybe` can be used
+fr: dans la plupart des langages impératifs.
+en: in most imperative languages.
+fr: En fait, c'est le type de construction que nous faisons naturellement.
+en: In fact, this is the kind of construction we make naturally.
+
+fr: > Une remarque importante :
+en: > An important remark:
+ >
+fr: > Le premier élement de la séquence qui sera évalué comme `Nothing` stoppera
+en: > The first element in the sequence being evaluated to `Nothing` will stop
+fr: > l'évaluation.
+en: > the complete evaluation.
+fr: > Cela signifie que vous n'exécutez pas toutes les lignes.
+en: > This means you don't execute all lines.
+fr: > Cela découle du caractère paresseux de Haskell.
+en: > You get this for free, thanks to laziness.
+
+fr: Vous pourriez aussi revoir ces exemples avec la définition de `(>>=)` pour `Maybe`
+en: You could also replay these example with the definition of `(>>=)` for `Maybe`
+fr: en tête :
+en: in mind:
+
+
+instance Monad Maybe where
+ (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
+ Nothing >>= _ = Nothing
+ (Just x) >>= f = f x
+
+ return x = Just x
+
+
+
+fr: La monade `Maybe` a prouvé par un simple exemple qu'elle est utile.
+en: The `Maybe` monad proved to be useful while being a very simple example.
+fr: Nous avons vu l'utilité de la monade `IO`.
+en: We saw the utility of the `IO` monad.
+fr: Mais maintenant, voici un exemple encore plus cool : les listes.
+en: But now for a cooler example, lists.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs
new file mode 100644
index 0000000..e32e638
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs
@@ -0,0 +1,67 @@
+fr:
La monade List
+en:
The list monad
+
+blogimage("golconde.jpg","Golconde de Magritte")
+
+fr: La monade `List` nous aide à simuler des calculs non-déterministes.
+en: The list monad helps us to simulate non-deterministic computations.
+fr: C'est parti :
+en: Here we go:
+
+> 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
+
+
+fr: Ma. GIQUE. :
+en: MA. GIC. :
+
+~~~
+[(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)]
+~~~
+
+fr: Pour la monade `List`, il y a aussi un sucre syntaxique :
+en: For the list monad, there is also this syntactic sugar:
+
+> print $ [ (x,y,z) | x <- allCases,
+> y <- allCases,
+> z <- allCases,
+> 4*x + 2*y < z ]
+
+fr: Je ne listerai pas toutes les monades, car il y en a beaucoup.
+en: I won't list all the monads, since there are many of them.
+fr: Utiliser les monades simplifie la manipulations de plusieurs notions dans les langages purs.
+en: Using monads simplifies the manipulation of several notions in pure languages.
+fr: Les monades sont très utiles, en particulier pour :
+en: In particular, monads are very useful for:
+
+fr: - L'E/S (IO),
+en: - IO,
+fr: - les calculs non-déterministes,
+en: - non-deterministic computation,
+fr: - générer des nombres pseudo-aléatoires,
+en: - generating pseudo random numbers,
+fr: - garder un état de configuration,
+en: - keeping configuration state,
+fr: - écrire un état,
+en: - writing state,
+- ...
+
+fr: Si vous m'avez suivi jusqu'ici, alors vous avez terminé !
+en: If you have followed me until here, then you've done it!
+fr: Vous connaissez les monades[^03021301] !
+en: You know monads[^03021301]!
+
+fr: [^03021301]: Vous aurez quand même besoin de pratiquer un peu pour vous habituer à elles et pour comprendre quand les utiliser et créer les vôtres. Mais vous avez déjà fait un grand pas dans cette direction.
+en: [^03021301]: 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/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs
new file mode 100644
index 0000000..61f2030
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs
@@ -0,0 +1,7 @@
+fr:
Appendice
+en:
Appendix
+
+fr: Cette section n'est pas vraiment sur l'apprentissage d'Haskell.
+en: This section is not so much about learning Haskell.
+fr: Elle est ici pour discuter de quelques détails.
+en: It is just here to discuss some details further.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
new file mode 100644
index 0000000..7d1f01f
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
@@ -0,0 +1,160 @@
+fr:
Revenons sur les arbres infinis
+en:
More on Infinite Tree
+
+fr: Dans la section sur [les structures infinies](#infinite-structures) nous avons vu quelques
+en: In the section [Infinite Structures](#infinite-structures) we saw some simple
+fr: constructions simples.
+en: constructions.
+fr: Malheureusement, nous avons enlevé deux propriétés de notre arbre:
+en: Unfortunately we removed two properties from our tree:
+
+fr: 1. Pas de valeurs identiques
+en: 1. no duplicate node value
+fr: 2. Arbre bien ordonné
+en: 2. well ordered tree
+
+fr: Dans cette section nous allons tenter de garder la première propriété.
+en: In this section we will try to keep the first property.
+fr: Concernant la seconde, nous ne devons pas nous en préoccuper ici mais nous discuterons
+en: Concerning the second one, we must relax it but we'll discuss how to
+fr: de comment la garder le plus possible.
+en: keep it as much as possible.
+
+
+
+This code is mostly the same as the one in the [tree section](#trees).
+
+> 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"
+>
+
+
+
+fr: Notre première étape est de créer une liste de nombres pseudo-aléatoires:
+en: Our first step is to create some pseudo-random number list:
+
+> shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
+
+fr: Pour mémoire, voici la définition de `treeFromList`
+en: Just as a reminder, here is the definition of `treeFromList`
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+fr: et
+en: and
+`treeTakeDepth`:
+
+> 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
+
+fr: Voyez le résultats de:
+en: See the result of:
+
+> main = do
+> putStrLn "take 10 shuffle"
+> print $ take 10 shuffle
+> putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
+> print $ treeTakeDepth 4 (treeFromList shuffle)
+
+~~~
+% 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
+~~~
+
+fr: Le code fonctionne!
+en: Yay! It ends!
+fr: Attention cependant, cela marchere seulement si vous avez toujours quelque chose à mettre dans une branche.
+en: Beware though, it will only work if you always have something to put into a branch.
+
+fr: Par exemple
+en: For example
+
+
+treeTakeDepth 4 (treeFromList [1..])
+
+
+fr: tournera en boucle pour toujours.
+en: will loop forever.
+fr: Simplement parce que le code essayera d'accéder à première valeur de `filter (<1) [2..]`.
+en: Simply because it will try to access the head of `filter (<1) [2..]`.
+fr: Mais `filter` n'est pas assez intelligent pour comprendre que le résultat est une liste vide.
+en: But `filter` is not smart enought to understand that the result is the empty list.
+
+fr: Toutefois, cela reste un exemple sympa de ce qu'un programme non-stricit a à offrir.
+en: Nonetheless, it is still a very cool example of what non strict programs have to offer.
+
+fr: Laissé pour exercice au lecteur:
+en: Left as an exercise to the reader:
+
+fr: - Prouver l'existence d'un nombre `n` tel que `treeTakeDepth n (treeFromList shuffle)` provoquera une boucle infinie.
+en: - Prove the existence of a number `n` so that `treeTakeDepth n (treeFromList shuffle)` will enter an infinite loop.
+fr: - Trouver une borne supérieur `n`.
+en: - Find an upper bound for `n`.
+fr: - Prouver qu'il n(y a pas de liste `shuffle` qui termine le programme pour n'importe quelle profondeur.
+en: - Prove there is no `shuffle` list so that, for any depth, the program ends.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
new file mode 100644
index 0000000..13e2b44
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
@@ -0,0 +1,192 @@
+
+
+
+This code is mostly the same as the preceding one.
+
+> import Debug.Trace (trace)
+> 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"
+>
+> 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
+
+
+
+fr: Pour résoudre ces problèmes nous allons modifier légèrement nos
+en: In order to resolve these problem we will modify slightly our
+fr: fonctions `treeFromList` et `shuffle`.
+en: `treeFromList` and `shuffle` function.
+
+fr: Un premier problème est le manque de nombres différents dans notre immlémentation de `shuffle`.
+en: A first problem, is the lack of infinite different number in our implementation of `shuffle`.
+fr: Nous avons généré seulement `4331` nombres différents.
+en: We generated only `4331` different numbers.
+fr: Pour résoudre cela nous allons faire un meilleure fonction `shuffle`.
+en: To resolve this we make a slightly better `shuffle` function.
+
+> 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
+
+fr: Cette fonction à la propriété de ne pas avoir de bornes supérieure ou inférieure.
+en: This shuffle function has the property (hopefully) not to have an upper nor lower bound.
+fr: Mais avoir une meilleure list `shuffle` n'est pas assez pour entrer dans une boucle infinie.
+en: But having a better shuffle list isn't enough not to enter an infinite loop.
+
+fr: Généralement, nous ne pouvons pas décider que `filter ( Tous les élements de la branche de gauche doit être strictement inférieur au la valeur racine.
+en: > Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.
+
+fr: Remarquez que cela donnera _souvent_ un arbre ordonné.
+en: Remark it will remains _mostly_ an ordered binary tree.
+fr: En outre, avec cette construction, chaque noeud est unique dans l'arbre.
+en: Furthermore, by construction, each node value is unique in the tree.
+
+fr: Voici notre nouvelle version de `treeFromList`. Nous avons simplement remplacé `filter` par `safefilter`.
+en: Here is our new version of `treeFromList`. We simply have replaced `filter` by `safefilter`.
+
+> treeFromList :: (Ord a, Show a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x left right
+> where
+> left = treeFromList $ safefilter ( right = treeFromList $ safefilter (>x) xs
+
+fr: Cette nouvelle fonction `safefilter` est presque équivalente à `filter` mais n'entre pas dans des boucles infinies si le résultat est une liste finie.
+en: This new function `safefilter` is almost equivalent to `filter` but don't enter infinite loop if the result is a finite list.
+fr: Si elle ne peut pas trouver un élément pour lequel le test est vrai après 10000 étapes consécutives, alors elle considère que la recherche est finie.
+en: 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.
+
+> 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)
+
+fr: Maintenant faites tourner le programme et soyez heureux:
+en: Now run the program and be happy:
+
+> main = do
+> putStrLn "take 10 shuffle"
+> print $ take 10 shuffle
+> putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
+> print $ treeTakeDepth 8 (treeFromList $ shuffle)
+
+fr: Vous devriez réaliser que le temps nécessaire pour afficher chaque valeur est différent.
+en: You should realize the time to print each value is different.
+fr: C'est parce que Haskell calcule chaque valeur lorsqu'il en a besoin.
+en: This is because Haskell compute each value when it needs it.
+fr: Et dans ce cas, il est demandé de l'afficher à l'écran.
+en: And in this case, this is when asked to print it on the screen.
+
+fr: Vous pouvez même essayer de remplacer la profondeur de `8` par `100`.
+en: Impressively enough, try to replace the depth from `8` to `100`.
+fr: Cela marchera sans tuer votre RAM!
+en: It will work without killing your RAM!
+fr: La gestion de la mémoire est faite naturellement par Haskell.
+en: The flow and the memory management is done naturally by Haskell.
+
+fr: Laissé comme exercices au lecteur:
+en: Left as an exercise to the reader:
+
+fr: - Même avec une grande valeur constante pour `deep` et `nbTry`, cela semble marcher correctement. Mais dans le pire des cas, cela peut devenir exponentiel.
+en: - Even with large constant value for `deep` and `nbTry`, it seems to work nicely. But in the worst case, it can be exponential.
+fr: Créez la pire liste à donner comme paramètre à `treeFromList`.
+en: Create a worst case list to give as parameter to `treeFromList`.
+fr: _indice_: pensez à (`[0,-1,-1,....,-1,1,-1,...,-1,1,...]`).
+en: _hint_: think about (`[0,-1,-1,....,-1,1,-1,...,-1,1,...]`).
+fr: - J'ai commencé à implémenter `safefilter` comme ceci:
+en: - I first tried to implement `safefilter` as follow:
+
+ safefilter' f l = if filter f (take 10000 l) == []
+ then []
+ else filter f l
+
+fr: Expliquer pourquoi cela ne fonctionne pas et peut entrer dans une boucle infinie.
+en: Explain why it doesn't work and can enter into an infinite loop.
+fr: - Supposez que `shuffle` est une liste de nombre réellement aléatoires avec de plus en plus de bornes.
+en: - Suppose that `shuffle` is real random list with growing bounds.
+fr: Si vous étudiez un peu cette structure, vous découvrirez qu'elle a toutes les chances
+en: If you study a bit this structure, you'll discover that with probability 1,
+fr: d'être finie.
+en: this structure is finite.
+fr: En utilisant le code suivant
+en: Using the following code
+fr: (supposez que nous pouvons utliser `safefilter'` directement comme si cela n'était pas dans le `where` de `safefilter`.
+en: (suppose we could use `safefilter'` directly as if was not in the where of safefilter)
+fr: trouvez une définition de `f` telle que, avec une probabilité de `1`,
+en: find a definition of `f` such that with probability `1`,
+fr: `treeFromList' shuffle` est infinie?. Et prouvez-le.
+en: `treeFromList' shuffle` is infinite. And prove it.
+fr: Avertissement, ce n'est qu'une conjecture.
+en: Disclaimer, this is only a conjecture.
+
+
+treeFromList' [] n = Empty
+treeFromList' (x:xs) n = Node x left right
+ where
+ left = treeFromList' (safefilter' (x) xs (f n)
+ f = ???
+
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs
new file mode 100644
index 0000000..7e11e0c
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs
@@ -0,0 +1,15 @@
+en: ## Thanks
+fr: ## Remerciements
+
+fr: Merci à [`/r/haskell`](http://reddit.com/r/haskell) et
+en: Thanks to [`/r/haskell`](http://reddit.com/r/haskell) and
+[`/r/programming`](http://reddit.com/r/programming).
+fr: Vos commentaires étaient plus que bienvenus.
+en: Your comment were most than welcome.
+
+fr: Particulièrement, je voudrais remercier mille fois [Emm](https://github.com/Emm)
+en: Particularly, I want to thank [Emm](https://github.com/Emm) a thousand times
+fr: pour le temps qu'il a consacré à corriger mon anglais.
+en: for the time he spent on correcting my English.
+fr: Merci beaucoup.
+en: Thank you man.
diff --git a/src/Scratch/en/blog/Haskell-the-Hard-Way/index.html b/src/Scratch/en/blog/Haskell-the-Hard-Way/index.html
new file mode 100644
index 0000000..455db6e
--- /dev/null
+++ b/src/Scratch/en/blog/Haskell-the-Hard-Way/index.html
@@ -0,0 +1,2388 @@
+
+
+
+
+ YBlog - Learn Haskell Fast and Hard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.
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 “Learn You a Haskell” and just after “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
+
+
+
+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
+
+runhaskell filename.lhs
+
+
Some might not work, but most will. You should see a link just below.
There are different way to install Haskell, I would recommend to use stack.
+
There are other way to install Haskell on your system you could visit, you can learn more about it by visiting haskell.org or 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
+
+
+
+
+
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”.
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:
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:
+
+
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.
+
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.
+
+Function declaration
+
+
You might be used to declaring functions like this:
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.
+
+A Type Example
+
+
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.
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)
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:
+
+% 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
+
+
Uh? What is this strange type?
+
Num a => a -> a -> a
+
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:
+
f :: Num a => a -> a -> a
+
+g :: Num a => a -> a
+g = f 3
+
+g y ⇔ 3*3 + y*y
+
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:
+
g = \y -> 3*3 + y*y
+
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.
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.
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: WAT
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.
+
+Notations
+
+
+Arithmetic
+
+
3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3)
+
+Logic
+
+
True || False ⇒ True
+True && False ⇒ False
+True == False ⇒ False
+True /= False ⇒ True (/=) is the operator for different
+
+Powers
+
+
x^n for n an integral (understand Int or Integer)
+x**y for y any kind of number (Float for example)
+
Integer has no limit except the capacity of your machine:
[] ⇔ 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]
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.
+
+
+Tuples
+
+
The type of couple is (a,b). Elements in a tuple can have different types.
+
-- 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
+
+Deal with parentheses
+
+
To remove some parentheses you can use two functions: ($) and (.).
+
-- 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))
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
+
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.
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:
+
+
Given a list of integers, return the sum of the even numbers in the list.
+
example: [1,2,3,4,5] ⇒ 2 + 4 ⇒ 6
+
+
To show differences between functional and imperative approaches, I’ll start by providing an imperative solution (in JavaScript):
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.
+
+
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.
+
+
Here is a C version of the recursive function. Note that for simplicity I assume the int list ends with the first 0 value.
To make things even better we should use higher order functions. What are these beasts? Higher order functions are functions taking functions as parameters.
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:
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.
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.
We just had to add another “transformation function”[^0216].
+
map (^2) [1,2,3,4] ⇔ [1,4,9,16]
+
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 ☺.
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.
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.
+
+
+
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.
+
+Type inference
+
+
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:
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:
C++ does a far better job than C in this regard. But for more complex functions the syntax can be hard to follow: see 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:
+
+
“if it compiles it certainly does what you intended”
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.
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.
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.
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:
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.
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.
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!
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:
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.
A typical function doing IO looks a lot like an imperative program:
+
f :: IO a
+f = do
+ x <- action1
+ action2 x
+ y <- action3
+ action4 x y
+
+
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.
+
+
+
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?
+
+
Ask a user to enter a list of numbers. Print the sum of the numbers
We should also pay attention to the effect of the <- symbol.
+
do
+ x <- something
+
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:
+
action1 :: IO a
+ -- in this case, generally a = ()
+
ou
+
value <- action2 -- where
+ -- action2 :: IO b
+ -- value :: b
+
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.
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 read4, but if something goes wrong the returned value is Nothing. If the value is right, it returns Just <the value>. Don’t try to understand too much of this function. I use a lower level function than read: reads.
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].
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.
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 is an [Integer] inside an IO»
+
+
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.
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 useIO.
+
+
Exercises:
+
+
Make a program that sums all of its arguments. Hint: use the function getArgs.
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:
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 pure5.
+
Haskell considers the state of the world as an input variable to main. But the real type of main is closer to this one6:
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:
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:
With, of course: actionN w :: (World) -> (a,World).
+
+
IMPORTANT: there are only two important patterns to consider:
+
let (x,w1) = action1 w0 in
+let (y,w2) = action2 x w1 in
+
and
+
let (_,w1) = action1 w0 in
+let (y,w2) = action2 w1 in
+
+
+
+
+
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:
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:
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.
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.
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.
+
+
Important remarks:
+
+
Monad are not necessarily about effects! There are a lot of pure monads.
+
Monad are more about sequencing
+
+
+
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):
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:
+
+
+
+
return a >>= k == k a
+m >>= return == m
+m >>= (\x -> k x >>= h) == (m >>= k) >>= h
+
+
+Maybe is a monad
+
+
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.
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.
+
+
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.
+
+
You could also replay these example with the definition of (>>=) for Maybe in mind:
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 monads7!
In the section Infinite Structures we saw some simple constructions. Unfortunately we removed two properties from our tree:
+
+
no duplicate node value
+
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 tree section.
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.
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.
This shuffle function has the property (hopefully) not to have an upper nor lower bound. But having a better shuffle list isn’t enough not to enter an infinite loop.
+
Generally, we cannot decide whether filter (<x) xs is empty. Then to resolve this problem, I’ll authorize some error in the creation of our binary tree. This new version of code can create binary tree which don’t have the following property for some of its nodes:
+
+
Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.
+
+
Remark it will remains mostly an ordered binary tree. Furthermore, by construction, each node value is unique in the tree.
+
Here is our new version of treeFromList. We simply have replaced filter by safefilter.
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.
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:
+
+safefilter' f l = if filter f (take 10000 l) == []
+ then []
+ else filter f l
+
+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.
Particularly, I want to thank Emm a thousand times for the time he spent on correcting my English. Thank you man.
+
+
+
+
Even if most recent languages try to hide them, they are present.↩
+
I know I’m cheating. But I will talk about non-strictness later.↩
+
For the brave, a more complete explanation of pattern matching can be found here.↩
+
Which is itself very similar to the javascript eval function, that is applied to a string containing JSON.↩
+
There are some unsafe exceptions to this rule. But you shouldn’t see such use in a real application except maybe for debugging purposes.↩
+
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.↩
+
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.↩
tl;dr: Few days ago there were about 20 job offer for Haskell. In only one day! How is that possible? As a real haskeller, I find this situation unbearable!
+
After all, we must avoid success at all cost. And I’ll help SPJ achieve this honorable goal.
+
+
Prevent Interest from beginner
+
Imagine a situation were you see people demonstrating some interest in learning Haskell.
+
Quick! Prevent them from going further.
+
If they come from the dynamic (uni-typed) languages like Python, Javascript…:
+
+
Haskell? A statically typed language??? Hmm… You mean like C and Java?
+
+
Such a remark should immediately shut down any interest in Haskell.
+
If they want to produce application with them:
+
+
Haskell? Isn’t it only a language for student! I don’t think it is useful for REAL WORLD applications!
+
+
If they just want to learn something new:
+
+
Haskell? Ah yes, I remember, mostly they only have the equivalent of Java interfaces and they stopped there. They don’t even have classes!!!! Can you imagine? I don’t even speak about class inheritance.
+
We’re in 2016! And they don’t even support basic Object Oriented Programming. What a joke!
+
+
If they love low level programming:
+
+
Haskell? Ah yes, I heard that lazyness make it impossible to think about code complexity and generally cause lot of space leaks.
+
+
And if it is not enough:
+
+
Haskell? Ah yes. I’m not fan of their Stop the World GC.
+
+
If they come from LISP and the statically typed language remark wasn’t enough. Try to mention the lack of macros in Haskell. Don’t mention template Haskell or even less Generics and all recent progress in GHC.
+
Make it difficult to install
+
Many hints there:
+
+
Send them on another compiler than GHC
+
Explain that they should never use a binary distribution of GHC! And they must compile it manually! It might not stop them but it will make the installation process much more tedious.
+
Lie! Explain there is a severe security issue with latest tools. Explain they must use cabal-install 1.18 or older.
+
Also explain them that in order to be able to handle lib dependencies correctly they MUST first learn Nix! Never talk about stack, cabal freeze, … While Nix is great, forcing new user completely alien to all these concepts to first learn it before starting to write their first line of code can greatly reduce their enthusiasm. Bonus point if you make them believe you can only program in Haskell on NixOS.
+
+
Make it difficult to learn
+
Make new comers feel dumb
+
The very first thing to do is to explain how Haskell is so easy to learn. How natural it is for everybody you know. And except someone you always considered very dumb, everybody was very productive in Haskell in few hours.
+
Use vocabulary alien to them as much as possible. Here is a list of terms you should use in the very first minutes of your description of Haskell:
+
+
catamorphism (bonus if you mention that the word come from the Greek κατα for catastrophe, that way you’ll look like a snob and you also use the word catastrophe in a Haskell context).
+
Monad! Of course you should use it ASAP! And explain they are exactly like potatoes or bananas shaped chairs. Double bonus if you explain that monad are really simple as they are just a monoid in the category of endofunctors.
+
GADTs
+
Yoneda Lemma
+
Homotopy Type Theory
+
…
+
+
Each of this term will hopefully be intimidating.
+
Tutorial authors
+
Please don’t provide an obvious first example like:
This nice example should overflow the number of new concepts a Haskell newcomer should deal with:
+
+
Language extensions. Each extension can take a lot of time to be explained.
+
Strange notations:
+
+
:<|>
+
'[] instead of []
+
+
Proxy
+
Immediate usage of $
+
deriving ha ha! You’ll need to explain typeclasses first!
+
the definition for getItemById
+
+
Of course use the most of your energy explaining the language extensions first. Use a great deal of details and if possible use as much as possible references to Category Theory. You’ll get bonus points if you mention HoTT! Double bonus points if you explain that understanding all details in HoTT is essential to use Haskell on a daily basis.
+
Explain that what this does is incredible but for the wrong reasons. For example don’t mention why instance ToJSON Item is great. But insist that we achieved to serve a JSON with extreme elegance and simplicity. Keep insisting on the simplicity and forgot to mention type safety which is one of the main benefit of Servant.
+
If you’re afraid that this example might be too close to a real world product, you can simply use some advanced lenses examples:
Certainly a great example to start a new language with.
+
Library authors
+
+
Do your best not to respect versioning number policy to maximize the probability to break things.
+
Don’t write any documentation, type are enough!
+
Even better, add mistakes to your documentation
+
Each time you can use a meaningful notation, make it wrong. For example, if you have a symmetric relation use an asymmetric symbol to represent it.
+
If possible remove all function names and only use symbols of at least 5 letters: For example you can replace your function log :: Level -> String -> IO () by (<=.=$$.).
+
+
If the the trends continue toward growth, then we might need to go further at the risk of breaking our own ecosystem:
+
+
Split your libs as much as possible. The best would be to use one lib by symbol
+
Use unsafePerformIO as much as possible
+
Push to Hackage a version not accessible on your public repository
+
modify the package on Hackage using the same version but with incompatible API
+
Add memory leaks
+
Add bugs
+
Add back doors and publish how to use them
+
+
Yes we said, at all cost!
+
Conclusion & Mistake
+
So with all of this I believe we should be on the right track to avoid success at all cost!
+
Sorry? What?
+
Oh… Apparently I made a precedence mistake!
+
SPJ didn’t asked to avoid success $ at all cost but to avoid $ success at all cost1.
+
Sorry! My bad! Forget about all of this. Keep the good work everybody! Haskell is certainly one of the most awesome language in the world! Its community is also just great.
+
I’m really happy to see it growth every year. Thanks to all contributors making it possible to still have a lot of fun after many years using Haskell!
+
And the fact that in Haskell the right choice is preferred to the easiest choice, certainly helped.
tl;dr: some simple implementation of higher order function for zsh.
+
+
Why is it important to have these functions? Simply because, the more I programmed with zsh the more I tended to work using functional programming style.
+
The minimal to have better code are the functions map, filter and fold.
+
Let’s compare. First a program which convert all gif to png in many different directories of different projects.
Also, the first verstion is a bit easier to read. But the second one is clearly far superior in architecture. I don’t want to argue why here. Just believe me that the functional programming approach is superior.
A Haskell tutorial: from nothing to something useful
+
+
+
+
+
+
+
+
+
+
tl;dr: Learn how to start a new Haskell project. Translate a starter tool written in zsh in Haskell using its own result.
+
+
“Good Sir Knight, will you come with me to Camelot, and join us at the Round Table?”
+
+
In order to work properly with Haskell you need to initialize your environment. Typically, you need to use a cabal file, create some test for your code. Both, unit test and propositional testing (random and exhaustive up to a certain depth). You need to use git and generally hosting it on github. Also, it is recommended to use cabal sandboxes. And as bonus, an auto-update tool that recompile and retest on each file save.
+
In this article, we will create such an environment using a zsh script. Then we will write a Haskell project which does the same work as the zsh script. You will then see how to work in such an environment.
+
If you are starting to understand Haskell but consider yourself a beginner, this tutorial will show you how to make a real application using quite surprisingly a lot of features:
+
+
use colorized output
+
interact with a user in command line
+
read/write files
+
kind of parse a file (in fact, simply split it)
+
use a templating system (mustache: fill a data structure, write files)
+
make a HTTP GET request then parse the JSON answer and use it
+
use random
+
create a cabal package
+
add and use non source files to a cabal package
+
Test your code (both unit testing and property testing)
+
+
☞ zsh is by its nature more suitable to file manipulation. But the Haskell code is clearly more organized while quite terse for a multi-purpose language.
+
☞ holy-project is on hackage. It can be installed with cabal update && cabal install holy-project.
While the article is very good, I lacked some minor informations1. Inspired by it, I created a simple script to initialize a new Haskell project. During the process I improved some things a bit.
What does this script do that cabal init doesn’t do?
+
+
Use cabal sandbox
+
It initialize git with the right .gitignore file.
+
Use tasty to organize your tests (HUnit, QuickCheck and SmallCheck).
+
Use -Wall for ghc compilation.
+
Will make references to Holy Grail
+
Search your default github username via github api.
+
+
zsh really?
+
+
+
+
Developing the script in zsh was easy. But considering its size, it is worth to rewrite it in Haskell. Furthermore, it will be a good exercise.
+
Patricide
+
In a first time, we initialize a new Haskell project with holy-haskell.sh:
+
+> ./holy-haskell.sh
+Bridgekeeper: Stop!
+Bridgekeeper: Who would cross the Bridge of Death
+Bridgekeeper: must answer me these questions three,
+Bridgekeeper: ere the other side he see.
+You: Ask me the questions, bridgekeeper, I am not afraid.
+
+Bridgekeeper: What is the name of your project?
+> Holy project
+Bridgekeeper: What is your name? (Yann Esposito (Yogsototh))
+>
+Bridgekeeper: What is your email? (Yann.Esposito@gmail.com)
+>
+Bridgekeeper: What is your github user name? (yogsototh)
+>
+Bridgekeeper: What is your project in less than ten words?
+> Start your Haskell project with cabal, git and tests.
+Initialize git
+Initialized empty Git repository in .../holy-project/.git/
+Create files
+ .gitignore
+ holy-project.cabal
+ Setup.hs
+ LICENSE (MIT)
+ test/Test.hs
+ test/HolyProject/Swallow/Test.hs
+ src/HolyProject/Swallow.hs
+ test/HolyProject/Coconut/Test.hs
+ src/HolyProject/Coconut.hs
+ src/HolyProject.hs
+ src/Main.hs
+Cabal sandboxing, install and test
+...
+ many compilations lines
+...
+Running 1 test suites...
+Test suite Tests: RUNNING...
+Test suite Tests: PASS
+Test suite logged to: dist/test/holy-project-0.1.0.0-Tests.log
+1 of 1 test suites (1 of 1 test cases) passed.
+All Tests
+ Swallow
+ swallow test: OK
+ coconut
+ coconut: OK
+ coconut property: OK
+ 148 tests completed
+
+All 3 tests passed
+
+
+
+Bridgekeeper: What... is the air-speed velocity of an unladen swallow?
+You: What do you mean? An African or European swallow?
+Bridgekeeper: Huh? I... I don't know that.
+[the bridgekeeper is thrown over]
+Bridgekeeper: Auuuuuuuuuuuugh
+Sir Bedevere: How do you know so much about swallows?
+You: Well, you have to know these things when you're a king, you know.
+
+
The different steps are:
+
+
small introduction quotes
+
ask five questions – three question sir…
+
create the directory for the project
+
init git
+
create files
+
sandbox cabal
+
cabal install and test
+
run the test directly in the terminal
+
small goodbye quotes
+
+
Features to note:
+
+
color in the terminal
+
check some rules on the project name
+
random message if error
+
use ~/.gitconfig file in order to provide a default name and email.
+
use the github API which returns JSON to get the default github user name.
+
+
So, apparently nothing too difficult to achieve.
+
We should now have an initialized Haskell environment for us to work. The first thing you should do, is to go into this new directory and launch ‘./auto-update’ in some terminal. I personally use tmux on Linux or the splits in iTerm 2 on Mac OS X. Now, any modification of a source file will relaunch a compilation and a test.
The haskell version is made by hand where zsh already had a capitalize operation on string with many words. Here is the difference between the shell and haskell way (note I splitted the effect of concatMap as map and concat):
In Haskell, while possible, we shouldn’t put the file content in the source code. We have a relatively easy way to include external file in a cabal package. This is what we will be using.
+
Furthermore, we need a templating system to replace small part of the static file by computed values. For this task, I choose to use hastache, a Haskell implementation of Mustache templates3.
+
Add external files in a cabal project
+
Cabal provides a way to add files which are not source files to a package. You simply have to add a Data-Files: entry in the header of the cabal file:
Now we simply have to create our files at the specified path. Here is for example the first lines of the LICENSE file.
+
The MIT License (MIT)
+
+Copyright (c) {{year}}{{author}}
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+...
+
It will be up to our program to replace the {{year}} and {{author}} at runtime. We have to find the files. Cabal will create a module named Paths_holy_project. If we import this module we have the function genDataFileName at our disposal. Now we can read the files at runtime like this:
A first remark is for portability purpose we shouldn’t use String for file path. For example on Windows / isn’t considered as a subdirectory character. To resolve this problem we will use FilePath:
In order to use hastache we can either create a context manually or use generics to create a context from a record. This is the last option we will show here. So in a first time, we need to import some modules and declare a record containing all necessary informations to create our project.
We use external files in mustache format. We ask question to our user to fill a data structure. We use this data structure to create a context. Hastache use this context with the external files to create the project files.
+
Git and Cabal
+
+
+
+
We need to initialize git and cabal. For this we simply call external command with the system function.
We want to retrieve the ~/.gitconfig file content and see if it contains a name and email information. We will need to access to the HOME environment variable. Also, as we use bytestring package for hastache, let’s take advantage of this library.
We could notice, getNameAndMail doesn’t read the full file and stop at the first occurrence of name and mail.
+
Use the github API
+
+
+
+
The task seems relatively easy, but we’ll see there will be some complexity hidden. Make a request on https://api.github.com/search/users?q=<email>. Parse the JSON and get the login field of the first item.
+
So the first problem to handle is to connect an URL. For this we will use the http-conduit package.
But, after some research, I discovered we must declare an User-Agent in the HTTP header to be accepted by the github API. So we have to change the HTTP Header, and our code became slightly more complex:
So now, we have a String containing a JSON representation. In javascript we would have used login=JSON.parse(body).items[0].login. How does Haskell will handle it (knowing the J in JSON is for Javascript)?
+
First we will need to add the lens-aeson package and use it that way:
It looks ugly, but it’s terse. In fact each function (^?), key and nth has some great mathematical properties and everything is type safe. Unfortunately I had to make my own jsonValueToString. I hope I simply missed a simpler existing function.
The HolyProject.hs file contains mostly the code that ask questions, show errors and copy files using hastache.
+
One of the benefits in modularizing the code is that our main code is clearer. Some functions are declared only in a module and are not exported. This help us hide technical details. For example, the modification of the HTTP header to use the github API.
+
Documenting
+
+
+
+
We didn’t take much advantage of the project structure yet. A first thing is to generate some documentation. Before most function I added comment starting with -- |. These comment will be used by haddock to create a documentation. First, you need to install haddock manually.
And magically, you’ll have a documentation in dist/doc/html/holy-project/index.html.
+
Tests
+
While the Haskell static typing is quite efficient to prevent entire classes of bugs, Haskell doesn’t discard the need to test to minimize the number of bugs.
+
Unit Testing with HUnit
+
+
+
+
It is generally said to test we should use unit testing for code in IO and QuickCheck or SmallCheck for pure code.
+
A unit test example on pure code is in the file test/HolyProject/Swallow/Test.hs:
Note swallow is (++). We group tests by group. Each group can contain some test suite. Here we have a test suite with only one test. The (@=?) verify the equality between its two parameters.
+
So now, we could safely delete the directory test/HolyProject/Swallow and the file src/HolyProject/Swallow.hs. And we are ready to make our own real world unit test. We will first test the module HolyProject.GithubAPI. Let’s create a file test/HolyProject/GithubAPI/Test.hs with the following content:
You have to modify your cabal file. More precisely, you have to add HolyProject.GithubAPI in the exposed modules of the library secion). You also have to update the test/Test.hs file to use GithubAPI instead of Swallow.
+
So we have our example of unit testing using IO. We search the github nickname for some people I know and we verify github continue to give the same answer as expected.
+
Property Testing with SmallCheck and QuickCheck
+
+
+
+
When it comes to pure code, a very good method is to use QuickCheck and SmallCheck. SmallCheck will verify all cases up to some depth about some property. While QuickCheck will verify some random cases.
+
As this kind of verification of property is mostly doable on pure code, we will test the StringUtils module.
+
So don’t forget to declare HolyProject.StringUtils in the exposed modules in the library section of your cabal file. Remove all references to the Coconut module.
+
Modify the test/Test.hs to remove all references about Coconut. Create a test/HolyProject/StringUtils/Test.hs file containing:
+All Tests
+ StringUtils
+ SC projectNameFromString idempotent: OK
+ 206 tests completed
+ SC capitalize idempotent: OK
+ 1237 tests completed
+ QC projectNameFromString idempotent: FAIL
+ *** Failed! Falsifiable (after 19 tests and 5 shrinks):
+ "a a"
+ Use --quickcheck-replay '18 913813783 2147483380' to reproduce.
+ GithubAPI
+ Yann: OK
+ Jasper: OK
+
+1 out of 5 tests failed
+
+
The test fail, but this is not an error. Our capitalize function shouldn’t be idempotent. I simply added this test to show what occurs when a test fail. If you want to look more closely to the error you could do this:
It is important to use ./interact instead of ghci. Because we need to tell ghci how to found the package installed.
+
Apparently, SmallCheck didn’t found any counter example. I don’t know how it generates Strings and using deeper search is really long.
+
Conclusion
+
+
+
+
Congratulation!
+
Now you could start programming in Haskell and publish your own cabal package.
+
+
+
+
For example, you have to install the test libraries manually to use cabal test.↩
+
There is no easy way to do something like name=$(ask name). Simply because $(ask name) run in another process which doesn’t get access to the standard input↩
+
Having a good level of power in templates is very difficult. imho Mustache has made the best compromise.↩
tl;dr: You want to teach yourself vim (the best text editor known to human kind) in the fastest way possible. This is my way of doing it. You start by learning the minimal to survive, then you integrate all the tricks slowly.
Learn vim and it will be your last text editor. There isn’t any better text editor that I know of. It is hard to learn, but incredible to use.
+
I suggest you teach yourself Vim in 4 steps:
+
+
Survive
+
Feel comfortable
+
Feel Better, Stronger, Faster
+
Use superpowers of vim
+
+
By the end of this journey, you’ll become a vim superstar.
+
But before we start, just a warning. Learning vim will be painful at first. It will take time. It will be a lot like playing a musical instrument. Don’t expect to be more efficient with vim than with another editor in less than 3 days. In fact it will certainly take 2 weeks instead of 3 days.
In a standard editor, typing on the keyboard is enough to write something and see it on the screen. Not this time. Vim is in Normal mode. Let’s go to Insert mode. Type the letter i.
+
You should feel a bit better. You can type letters like in a standard editor. To get back to Normal mode just press the ESC key.
+
You now know how to switch between Insert and Normal mode. And now, here are the commands that you need in order to survive in Normal mode:
+
+
+
i → Insert mode. Type ESC to return to Normal mode.
+
x → Delete the char under the cursor
+
:wq → Save and Quit (:w save, :q quit)
+
dd → Delete (and copy) the current line
+
p → Paste
+
+
Recommended:
+
+
hjkl (highly recommended but not mandatory) → basic cursor move (←↓↑→). Hint: j looks like a down arrow.
+
:help <command> → Show help about <command>. You can use :help without a <command> to get general help.
+
+
+
Only 5 commands. That is all you need to get started. Once these command start to become natural (maybe after a day or so), you should move on to level 2.
+
But first, just a little remark about Normal mode. In standard editors, to copy you have to use the Ctrl key (Ctrl-c generally). In fact, when you press Ctrl, it is as if all of your keys change meaning. Using vim in normal mode is a bit like having the editor automatically press the Ctrl key for you.
+
A last word about notations:
+
+
instead of writing Ctrl-λ, I’ll write <C-λ>.
+
commands starting with : end with <enter>. For example, when I write :q, I mean :q<enter>.
+
+
2nd Level – Feel comfortable
+
You know the commands required for survival. It’s time to learn a few more commands. These are my suggestions:
+
+
Insert mode variations:
+
+
+
a → insert after the cursor
+
o → insert a new line after the current one
+
O → insert a new line before the current one
+
cw → replace from the cursor to the end of the word
+
+
+
Basic moves
+
+
+
0 → go to the first column
+
^ → go to the first non-blank character of the line
+
$ → go to the end of line
+
g_ → go to the last non-blank character of line
+
/pattern → search for pattern
+
+
+
Copy/Paste
+
+
+
P → paste before, remember p is paste after current position.
+
yy → copy the current line, easier but equivalent to ddP
+
+
+
Undo/Redo
+
+
+
u → undo
+
<C-r> → redo
+
+
+
Load/Save/Quit/Change File (Buffer)
+
+
+
:e <path/to/file> → open
+
:w → save
+
:saveas <path/to/file> → save to <path/to/file>
+
:x, ZZ or :wq → save and quit (:x only save if necessary)
+
:q! → quit without saving, also: :qa! to quit even if there are modified hidden buffers.
+
:bn (resp. :bp) → show next (resp. previous) file (buffer)
+
+
+
+
Take the time to learn all of these command. Once done, you should be able to do every thing you are able to do in other editors. You may still feel a bit awkward. But follow me to the next level and you’ll see why vim is worth the extra work.
+
3rd Level – Better. Stronger. Faster.
+
Congratulation for reaching this far! Now we can start with the interesting stuff. At level 3, we’ll only talk about commands which are compatible with the old vi editor.
+
Better
+
Let’s look at how vim could help you to repeat yourself:
. → Just after the last command will write again the 100 “desu”.
+
3. → Will write 3 “desu” (and not 300, how clever).
+
+
+
Stronger
+
Knowing how to move efficiently with vim is very important. Don’t skip this section.
+
+
NG → Go to line N
+
gg → shortcut for 1G - go to the start of the file
+
G → Go to last line
+
Word moves:
+
+
+
w → go to the start of the following word,
+
e → go to the end of this word.
+
+
By default, words are composed of letters and the underscore character. Let’s call a WORD a group of letter separated by blank characters. If you want to consider WORDS, then just use uppercase characters:
+
+
W → go to the start of the following WORD,
+
E → go to the end of this WORD.
+
+
+
+
+
+
+
Now let’s talk about very efficient moves:
+
+
+
% : Go to the corresponding (, {, [.
+
* (resp. #) : go to next (resp. previous) occurrence of the word under the cursor
+
+
+
Believe me, the last three commands are gold.
+
Faster
+
Remember about the importance of vi moves? Here is the reason. Most commands can be used using the following general format:
+
<start position><command><end position>
+
For example : 0y$ means
+
+
0 → go to the beginning of this line
+
y → yank from here
+
$ → up to the end of this line
+
+
We also can do things like ye, yank from here to the end of the word. But also y2/foo yank up to the second occurrence of “foo”.
+
But what was true for y (yank), is also true for d (delete), v (visual select), gU (uppercase), gu (lowercase), etc…
+
4th Level – Vim Superpowers
+
With all preceding commands you should be comfortable using vim. But now, here are the killer features. Some of these features were the reason I started to use vim.
+
Move on current line: 0^$g_fFtT,;
+
+
+
0 → go to column 0
+
^ → go to first character on the line
+
$ → go to the last column
+
g_ → go to the last character on the line
+
fa → go to next occurrence of the letter a on the line. , (resp. ;) will find the next (resp. previous) occurrence.
+
t, → go to just before the character ,.
+
3fa → find the 3rd occurrence of a on this line.
+
F and T → like f and t but backward.
+
+
+
+
+
+
A useful tip is: dt" → remove everything until the ".
+
Zone selection <action>a<object> or <action>i<object>
+
These command can only be used after an operator in visual mode. But they are very powerful. Their main pattern is:
+
<action>a<object> and <action>i<object>
+
Where action can be any action, for example, d (delete), y (yank), v (select in visual mode). The object can be: w a word, W a WORD (extended word), s a sentence, p a paragraph. But also, natural character such as ", ', ), }, ].
+
Suppose the cursor is on the first o of (map (+) ("foo")).
+
+
+
vi" → will select foo.
+
va" → will select "foo".
+
vi) → will select "foo".
+
va) → will select ("foo").
+
v2i) → will select map (+) ("foo")
+
v2a) → will select (map (+) ("foo"))
+
+
+
+
+
+
Select rectangular blocks: <C-v>.
+
Rectangular blocks are very useful for commenting many lines of code. Typically: 0<C-v><C-d>I-- [ESC]
+
+
^ → go to the first non-blank character of the line
+
<C-v> → Start block selection
+
<C-d> → move down (could also be jjj or %, etc…)
+
I-- [ESC] → write -- to comment each line
+
+
+
+
+
Note: in Windows you might have to use <C-q> instead of <C-v> if your clipboard is not empty.
+
Completion: <C-n> and <C-p>.
+
In Insert mode, just type the start of a word, then type <C-p>, magic…
+
+
+
+
Macros : qa do something q, @a, @@
+
qa record your actions in the registera. Then @a will replay the macro saved into the register a as if you typed it. @@ is a shortcut to replay the last executed macro.
+
+
Example
+
On a line containing only the number 1, type this:
+
+
qaYp<C-a>q →
+
+
qa start recording.
+
Yp duplicate this line.
+
<C-a> increment the number.
+
q stop recording.
+
+
@a → write 2 under the 1
+
@@ → write 3 under the 2
+
Now do 100@@ will create a list of increasing numbers until 103.
+
+
+
+
+
+
Visual selection: v,V,<C-v>
+
We saw an example with <C-v>. There is also v and V. Once the selection has been made, you can:
+
+
J → join all the lines together.
+
< (resp. >) → indent to the left (resp. to the right).
+
= → auto indent
+
+
+
+
+
Add something at the end of all visually selected lines:
+
+
<C-v>
+
go to desired line (jjj or <C-d> or /pattern or % etc…)
+
$ go to the end of the line
+
A, write text, ESC.
+
+
+
+
+
Splits: :split and vsplit.
+
These are the most important commands, but you should look at :help split.
+
+
+
:split → create a split (:vsplit create a vertical split)
+
<C-w><dir> : where dir is any of hjkl or ←↓↑→ to change the split.
+
<C-w>_ (resp. <C-w>|) : maximise the size of the split (resp. vertical split)
+
<C-w>+ (resp. <C-w>-) : Grow (resp. shrink) split
+
+
+
+
+
+
Conclusion
+
That was 90% of the commands I use every day. I suggest that you learn no more than one or two new commands per day. After two to three weeks you’ll start to feel the power of vim in your hands.
+
Learning Vim is more a matter of training than plain memorization. Fortunately vim comes with some very good tools and excellent documentation. Run vimtutor until you are familiar with most basic commands. Also, you should read this page carefully: :help usr_02.txt.
+
Then, you will learn about !, folds, registers, plugins and many other features. Learn vim like you’d learn piano and all should be fine.
+
+
If you liked this article, there is a follow up: Vim as IDE
tl;dr: How I manage safely my password with success for some years now.
+sha1( password + domain_name )
+I memorize only one password. I use a different password on all website.
+
+
Disclamer, this is an unashamed attempt to make you download my iPhone app ;-). You’re always here? Even if you won’t download my app, you should read more. My method doesn’t necessitate my app. It is both safe and easy to use everyday.
Imagine you find a really good password. You use it on GMail, Amazon, PayPal, Twitter, Facebook… One day you see a nice online game you want to try. They ask you your email and a password. Some week passes, and the host machine of this online game is hacked. Your mail and password is now in bad hands. Unfortunately for you, you use the same password everywhere. Then, the attacker can simply try your password everywhere. On PayPal for example.
+
Well now, how could we fix that?
+
Which methodology?
+
+
the good, the bad & the ugly
+
+
The mostly used method is to remember a subset of different passwords. In the best cases, your remember about 13 password. Some strong, some weak.
+
What to do if you use more online services than your memory can handle?
+
A bad solution would be to chose passwords like this:
+
+
twitter: P45sW0r|)Twitter
+
gmail: P45sW0r|)gmail
+
badonlinegame: P45sW0r|)badonlinegame
+
+
Unfortunately, if someone get your password on badonlinegame, he could easily find your other passwords. Of course you can imagine some better transformation. But it is hard to find a very good one.
+
Fortunately, there exists functions which handle exactly this problem. Hash Function. Knowing the result of a hash function, it is difficult to know what was their input. For example:
If someone has 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63, he will have hard time to recover P45sW0r|).
+
Let choose SHA1 as hash function. Now the password for any website should of the form:
+
sha1( master_password + domain_name ) ~~~~~~
+
Where:
+
+
master_password is your unique master password,
+
domain_name is the domain name of the website you want the password for,
+
+
+
But what about some website constraint? For example regarding the length of the password? What to do if you want to change your password? What to do if you want number or special characters? This is why, for each website I need some other parameters:
+
+
the login name
+
the password’s length,
+
the password number (in order to change it),
+
The output format: hexadecimal or base64.
+
+
In practice?
+
Depending on my situation here are the tools I made & use:
My password are at a copy/paste on all environment I use. I have some services for which I have password of 40 characters. Now I use 10 character for most of my passwords. Further more using shorter password make it even harder for an attaquer to retrieve my master password.
+
I would be happy to hear your thoughts on using this methodology.
tl;dr: Determine with the most objectivity possible the best(s) web framework(s) depending on your needs. Here are the results. Please note the actual ability to take a rational decision is pretty bad for now.
+
+
This is it.
+You’ve got the next big idea.
+You just need to make a very simple web application.
+
It sounds easy! You just need to choose a good modern web framework, when suddenly:
+
+
After your brain stack overflowed, you decide to use a very simple methodology. Answer two questions:
+
Which language am I familiar with?
+What is the most popular web framework for this language?
+
Great! This is it.
+
But, you continually hear this little voice.
+
+
“You didn’t made a bad choice, yes. But …
+you hadn’t made the best either.”
+
+
This article try to determine in the most objective and rational way the best(s) web framework(s) depending on your needs. To reach this goal, I will provide a decision tool in the result section.
+
I will use the following methodology:
+
Methodology
+
+
Model how to make choice
+
+
choose important parameters
+
organize (hierarchize) them
+
write down an objective chooser
+
+
Grab objective quantified informations about web frameworks relatively to choosen parameters
+
Sanitize your data in order to handle imprecisions, lack of informations…
+
Apply the model of choice to your informations
+
+
+
☞ Important Note
+I am far from happy to the actual result. There are a lot of biases, for example in the choice of the parameters. The same can be said about the data I gathered. I am using very imprecise informations. But, as far as I know, this is the only article which use many different parameters to help you choose a web framework.
Here are the important features (properties/parameters) I selected to make the choice:
+
+
Popularity, which correlate with:
+
+
number of tested libraries
+
facility to find learning material
+
ability to find another developer to work with
+
+
Efficiency, which is generally correlated to:
+
+
how much processing power you’ll need per user
+
maintenance price per user
+
how long the user will wait to see/update data
+
+
Expressiveness, which is generally correlated to:
+
+
faster development
+
flexibility, adaptability
+
+
Robustness, which correlate with:
+
+
security
+
fewer bugs
+
+
+
Each feature is quite important and mostly independant from each other. I tried to embrace most important topics concerning web frameworks with these four properties. I am fully concious some people might lack another important feature. Nonetheless the methodology used here can be easily replicated. If you lack an important property add it at will and use this choice method.
+
Also each feature is very hard to measure with precision. This is why we will only focus on order of magnitude.
+
For each property a framework could have one of the six possible values: Excellent, Very Good, Good, Medium, Bad or Very Bad
+
So how to make a decision model from these informations?
+
One of the most versatile method is to give a weight for each cluster value. And to select the framework maximizing this score:
We don’t make any difference between excellent and very good in popularity.
+
Concerning efficient framework in excellent cluster will have 2 more points than the “very good” cluster.
+
+
So for each framework we compute its score relatively to a weighted table. And we select the best(s).
+
Example: Using this hypothetic framework and the preceeding table.
+
+
+
+
+
Expressiveness
+
Popularity
+
Efficiency
+
Robustness
+
+
+
+
+
yog
+
Excellent
+
Very Bad
+
Medium
+
Very Good
+
+
+
+
score(yog) = 10 + 0 + 4 + 8 = 22
+
+
Most needs should be expressed by such a weighted table. In the result section, we will discuss this further.
+
It is now time to try to get these measures.
+
Objective measures
+
None of the four properties I choosen can be measured with perfect precision. But we could get the order of magnitude for each.
+
I tried to focus on the framework only. But it is often easier to start by studying the language first.
+
For example, I have datas about popularity by language and I also have different datas concerning popularity by framework. Even if I use only the framework focused datas in my final decision model, it seemed important to me to discuss about the datas for the languages. The goal is to provide a tool to help decision not to give a decision for you.
+
Popularity
+
RedMonk Programming Language Rankings (January 2013) provide an apparent good measure of popularity. While not perfect the current measure feel mostly right. They create an image using stack overflow and github data. Vertical correspond to the number of questions on stackoverflow. Horizontal correspond to the number of projects on github.
+
If you look at the image, your eye can see about four clusters. The 1st cluster correspond to mainstream languages:
+
+
Most developer know at least one of these language.
+
The second cluster is quite bigger. It seems to correspond to languages with a solid community behind them.
+
+
I don’t get into detail, but you could also see third and fourth tier popular languages.
I don’t thing I could find easily web frameworks for third or fourth tier languages.
+
For now, I only talked about language popularity. But what about framework popularity? I made a test using number of question on stackoverflow only. Then by dividing by two for each 6 cluster:
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
%
+
+
+
+
+
Excellent
+
Ruby
+
Rails
+
176208
+
100%
+
+
+
Very Good
+
Python
+
Django
+
57385
+
<50%
+
+
+
+
Java
+
Servlet
+
54139
+
+
+
+
+
Java
+
Spring
+
31641
+
+
+
+
+
Node.js
+
node.js
+
27243
+
+
+
+
+
PHP
+
Codeigniter
+
21503
+
+
+
+
+
Groovy
+
Grails
+
20222
+
+
+
+
Good
+
Ruby
+
Sinatra
+
8631
+
<25%
+
+
+
+
Python
+
Flask
+
7062
+
+
+
+
+
PHP
+
Laravel
+
6982
+
+
+
+
+
PHP
+
Kohana
+
5959
+
+
+
+
+
Node.js
+
Express
+
5009
+
+
+
+
Medium
+
PHP
+
Cake
+
4554
+
<13%
+
+
+
+
C♯
+
ServiceStack
+
3838
+
+
+
+
+
Scala
+
Play
+
3823
+
+
+
+
+
Java
+
Wicket
+
3819
+
+
+
+
+
Dart
+
Dart
+
3753
+
+
+
+
+
PHP
+
Slim
+
3361
+
+
+
+
+
Python
+
Tornado
+
3321
+
+
+
+
+
Scala
+
Lift
+
2844
+
+
+
+
+
Go
+
Go
+
2689
+
+
+
+
Bad
+
Java
+
Tapestry
+
1197
+
<6%
+
+
+
+
C♯
+
aspnet
+
1000
+
+
+
+
+
Haskell
+
Yesod
+
889
+
+
+
+
+
PHP
+
Silex
+
750
+
+
+
+
+
PHP
+
Lithium
+
732
+
+
+
+
+
C♯
+
nancy
+
705
+
+
+
+
Very bad
+
Java
+
Grizzly
+
622
+
<3%
+
+
+
+
Erlang
+
Cowboy
+
568
+
+
+
+
+
Perl
+
Dancer
+
496
+
+
+
+
+
PHP
+
Symphony2
+
491
+
+
+
+
+
Go
+
Revel
+
459
+
+
+
+
+
Clojure
+
Compojure
+
391
+
+
+
+
+
Perl
+
Mojolicious
+
376
+
+
+
+
+
Scala
+
Scalatra
+
349
+
+
+
+
+
Scala
+
Finagle
+
336
+
+
+
+
+
PHP
+
Phalcon
+
299
+
+
+
+
+
js
+
Ringo
+
299
+
+
+
+
+
Java
+
Gemini
+
276
+
+
+
+
+
Haskell
+
Snap
+
263
+
+
+
+
+
Perl
+
Plack
+
257
+
+
+
+
+
Erlang
+
Elli
+
230
+
+
+
+
+
Java
+
Dropwizard
+
188
+
+
+
+
+
PHP
+
Yaf
+
146
+
+
+
+
+
Java
+
Play1
+
133
+
+
+
+
+
Node.js
+
Hapi
+
131
+
+
+
+
+
Java
+
Vertx
+
60
+
+
+
+
+
Scala
+
Unfiltered
+
42
+
+
+
+
+
C
+
onion
+
18
+
+
+
+
+
Clojure
+
http-kit
+
17
+
+
+
+
+
Perl
+
Kelp
+
16
+
+
+
+
+
PHP
+
Micromvc
+
13
+
+
+
+
+
Lua
+
Openresty
+
8
+
+
+
+
+
C++
+
cpoll-cppsp
+
5
+
+
+
+
+
Clojure
+
Luminus
+
3
+
+
+
+
+
PHP
+
Phreeze
+
1
+
+
+
+
+
As we can see, our framework popularity indicator can be quite different from its language popularity. For now I didn’t found a nice way to merge the results from RedMonk with these one. So I’ll use these unperfect one. Hopefully the order of magninute is mostly correct for most framework.
+
Efficiency
+
Another objective measure is efficiency. We all know benchmarks are all flawed. But they are the only indicators concerning efficiency we have.
+
I used the benchmark from benchmarksgame. Mainly, there are five clusters:
+
+
+
+
1x→2x
+
C, C++
+
+
+
2x→3x
+
Java 7, Scala, OCamL, Haskell, Go, Common LISP
+
+
+
3x→10x
+
C♯, Clojure, Racket, Dart
+
+
+
10x→30x
+
Erlang
+
+
+
30x→
+
PHP, Python, Perl, Ruby, JRuby
+
+
+
+
Remarks concerning some very slow languages:
+
+
PHP ; huge variations, can be about 1.5x C speed in best case.
+
Python ; huge variations, can be about 1.5x C speed in best case
+
Perl ; Can be about 3x C speed in best case
+
Ruby, JRuby ; mostly very slow.
+
+
This is a first approach. The speed of the language for basic benchmarks. But, here we are interrested in web programming. Fortunately techempower has made some tests focused on most web frameworks:
These benchmark doesn’t fit well with our needs. The values are certainly quite imprecise to your real usage. The goal is just to get an order of magnitude for each framework. Another problem is the high number of informations.
+
As always, we should remember these informations are also imprecise. So I simply made some classes of efficiency.
+
Remark: I separated the clusters by using power of 2 relatively to the fastest.
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
slowness
+
+
+
+
+
Excellent
+
C++
+
cpoll-cppsp
+
114,711
+
1×
+
+
+
+
Jav
+
gemini
+
105,204
+
+
+
+
+
Lua
+
openresty
+
93,882
+
+
+
+
+
Jav
+
servlet
+
90,580
+
+
+
+
+
C++
+
cpoll-pool
+
89,167
+
+
+
+
+
Go
+
go
+
76,024
+
+
+
+
+
Sca
+
finagle
+
68,413
+
+
+
+
+
Go
+
revel
+
66,990
+
+
+
+
+
Jav
+
rest-express
+
63,209
+
+
+
+
Very Good
+
Jav
+
wicket
+
48,772
+
>2×
+
+
+
+
Sca
+
scalatra
+
48,594
+
+
+
+
+
Clj
+
http-kit
+
42,703
+
+
+
+
+
Jav
+
spring
+
36,643
+
>3×
+
+
+
+
PHP
+
php
+
36,605
+
+
+
+
+
Jav
+
tapestry
+
35,032
+
+
+
+
+
Clj
+
compojure
+
32,088
+
+
+
+
+
JS
+
ringo
+
31,962
+
+
+
+
+
Jav
+
dropwizard
+
31,514
+
+
+
+
+
Clj
+
luminus
+
30,672
+
+
+
+
Good
+
Sca
+
play-slick
+
29,950
+
>4×
+
+
+
+
Sca
+
unfiltered
+
29,782
+
+
+
+
+
Erl
+
elli
+
28,862
+
+
+
+
+
Jav
+
vertx
+
28,075
+
+
+
+
+
JS
+
nodejs
+
27,598
+
+
+
+
+
Erl
+
cowboy
+
24,669
+
+
+
+
+
C
+
onion
+
23,649
+
+
+
+
+
Hkl
+
yesod
+
23,304
+
+
+
+
+
JS
+
express
+
22,856
+
>5×
+
+
+
+
Sca
+
play-scala
+
22,372
+
+
+
+
+
Jav g
+
rizzly-jersey
+
20,550
+
+
+
+
+
Py
+
tornado
+
20,372
+
>6×
+
+
+
+
PHP
+
phalcon
+
18,481
+
+
+
+
+
Grv
+
grails
+
18,467
+
+
+
+
+
Prl
+
plack
+
16,647
+
>7×
+
+
+
+
PHP
+
yaf
+
14,388
+
+
+
+
Medium
+
JS
+
hapi
+
11,235
+
>10×
+
+
+
+
Jav
+
play1
+
9,979
+
+
+
+
+
Hkl
+
snap
+
9,196
+
+
+
+
+
Prl
+
kelp
+
8,250
+
+
+
+
+
Py
+
flask
+
8,167
+
+
+
+
+
Jav
+
play-java
+
7,905
+
+
+
+
+
Jav p
+
lay-java-jpa
+
7,846
+
+
+
+
+
PHP
+
micromvc
+
7,387
+
+
+
+
+
Prl
+
dancer
+
5,040
+
>20×
+
+
+
+
Prl
+
mojolicious
+
4,371
+
+
+
+
+
JS
+
ringo-conv
+
4,249
+
+
+
+
+
Py
+
django
+
4,026
+
+
+
+
+
PHP
+
codeigniter
+
3,809
+
>30×
+
+
+
Bad
+
Rby
+
rails
+
3,445
+
+
+
+
+
Sca
+
lift
+
3,311
+
+
+
+
+
PHP
+
slim
+
3,112
+
+
+
+
+
PHP
+
kohana
+
2,378
+
>40×
+
+
+
+
PHP
+
silex
+
2,364
+
+
+
+
Very Bad
+
PHP
+
laravel
+
1,639
+
>60×
+
+
+
+
PHP
+
phreeze
+
1,410
+
+
+
+
+
PHP
+
lithium
+
1,410
+
+
+
+
+
PHP
+
fuel
+
1,410
+
+
+
+
+
PHP
+
cake
+
1,287
+
>80×
+
+
+
+
PHP
+
symfony2
+
879
+
>100×
+
+
+
+
C#
+
aspnet-mvc
+
871
+
+
+
+
+
Rby
+
sinatra
+
561
+
>200×
+
+
+
+
C#
+
servicestack
+
51
+
+
+
+
+
Dar
+
dart
+
0
+
+
+
+
+
C#
+
nancy
+
0
+
+
+
+
+
Prl
+
web-simple
+
0
+
+
+
+
+
These are manually made clusters. But you get the idea. Certainly, some framework could jump between two different clusters. So this is something to remember. But as always, the order of magnitude is certainly mostly right.
+
Expressiveness
+
Now, how to objectively measure expressiveness?
+
RedMonk had a very good idea to find an objective (while imprecise) measure of each language expressiveness. Read this article for details.
+
After filtering languages suitable for web development, we end up with some clusters:
+
+
+
+
Cluster
+
Languages
+
+
+
+
+
Excellent
+
Coffeescript, Clojure, Haskell
+
+
+
Very Good
+
Racket, Groovy, R, Scala, OCamL, F♯, Erlang, Lisp, Go
+
+
+
Medium
+
Perl, Python, Objective-C, Scheme, Tcl, Ruby
+
+
+
Bad
+
Lua, Fortran (free-format), PHP, Java, C++, C♯
+
+
+
Very Bad
+
Assembly, C, Javascript,
+
+
+
+
Unfortunately there is no information about dart. So I simply give a very fast look at the syntax. As it looked a lot like javascript and js is quite low. I decided to put it close to java.
+
Also an important remark, javascript score very badly here while coffeescript (compiling to js) score “excellent”. So if you intend to use a javascript framework but only with coffescript that should change substantially the score. As I don’t believe it is the standard. Javascript oriented framework score very badly regarding expressiveness.
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Grv
+
grails
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Medium
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Bad
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
Very Bad
+
C
+
onion
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
+
Robustness
+
I couldn’t find any complete study to give the number of bug relatively to each framework/language.
+
But one thing I saw from experience is the more powerful the type system the safest your application is. While the type system doesn’t remove completely the need to test your application a very good type system tend to remove complete classes of bug.
+
Typically, not using pointer help to reduce the number of bugs due to bad references. Also, using a garbage collector, reduce greatly the probability to access unallocated space.
+
+
From my point of view, robustness is mostly identical to safety.
+
Here are the clusters:
+
+
+
+
Excellent
+
Haskell, Scheme, Erlang
+
+
+
Very Good
+
Scala, Java, Clojure
+
+
+
Good
+
Ruby, Python, Groovy, javascript, PHP
+
+
+
Medium
+
C++, C#, Perl, Objective-C, Go, C
+
+
+
+
So applying this to frameworks gives the following clusters:
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Good
+
Grv
+
grails
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Medium
+
C
+
onion
+
+
+
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
+
The result
+
For the result I initialized the table with my own needs.
+
And I am quite happy it confirms my current choice. I sware I didn’t given yesod any bonus point. I tried to be the most objective and factual as possible.
+
Now, it is up to you to enter your preferences.
+
On each line you could change how important a feature is for you. From essential to unsignificant. Of course you could change the matrix at will.
+
I just show a top 10 frameworks. In order to give a more understandable measure I provide the log of the score.
+
+
+
+
+
+Excellent
+
+
+Very good
+
+
+Good
+
+
+Medium
+
+
+Bad
+
+
+Very bad
+
+
+Importance
+
+
+
+
+Expressiveness
+
+
+
+
+Popularity
+
+
+
+
+Efficiency
+
+
+
+
+Robustness
+
+
+
+
+Click to force refresh
+
+
+
+
+
+
+
I didn’t had the courage in explaining in what the scoring system is good. Mostly, if you use product instead of sum for the score you could use power of e for the values in the matrix. And you could see the matrix as a probability matrix (each line sum to 1). Which provide a slighly better intuition on whats going on.
+
Remember only that values are exponential. Do not double an already big value for example the effect would be extreme.
+
Conclusion
+
All of this is based as most as I could on objective data. The choice method seems both rather rational and classical. It is now up to you to edit the score matrix to set your needs.
+
I know that in the current state there are many flaws. But it is a first system to help make a choice rationally.
+
I encourage you to go further if you are not satisfied by my method.
+
The source code for the matrix shouldn’t be too hard to read. Just read the source of this webpage. You could change the positionning of some frameworks if you believe I made some mistake by placing them in some bad clusters.
+
So I hope this tool will help you in making your life easier.
tl;dr: How to use m4 to increase the power of deficient languages. Two examples: improve xslt syntax and make fractal with svg.
+
+
xml was a very nice idea about structuring data. Some people where so enthusiastic about xml they saw it everywhere. The idea was: the future is xml. Then some believed it would be a good idea to invent many xml compatible format and even programming languages with xml syntax.
+
Happy! Happy! Joy! Joy!
+
Unfortunately, xml was made to transfert structured data. Not a format a human should see or edit directly. The sad reality is xml syntax is simply verbose and ugly. Most of the time it shouldn’t be a problem, as nobody should see it. In a perfect nice world, we should never deal directly with xml but only use software which deal with it for us. Guess what? Our world isn’t perfect. Too sad, a bunch of developer have to deal directly with this ugly xml.
+
Unfortunately xml isn’t the only case of misused format I know. You have many format for which it would be very nice to add variables, loops, functions…
+
If like me you hate with passion xslt or writing xml, I will show you how you could deal with this bad format or language.
+
The xslt Example
+
Let’s start by the worst case of misused xml I know: xslt. Any developer who had to deal with xslt know how horrible it is.
+
In order to reduce the verbosity of such a bad languages, there is a way. m4. Yes, the preprocessor you use when you program in C and C++.
+
Here are some example:
+
+
Variable, instead of writing the natural myvar = value, here is the xslt way of doing this:
Profit! Now xslt is more readable and easier to edit!
+
The cool part: Fractals!
+
svg is an xml format used to represent vector graphics, it even support animations. At its beginning some people believed it would be the new Flash. Apparently, it will be more canvas + js.
The positionning of the “esod” text with regards to the reversed “λ” was done by changing position in firebug. I didn’t had to manually regenerate to test.
+
Making such a fractal is mostly:
+
+
take a root element
+
duplicate and transform it (scaling, translating, rotate)
+
the result is a sub new element.
+
repeat from 2 but by taking the sub new element as new root.
+
Stop when recursion is deep enough.
+
+
If I had to do this for each step, I had make a lot of copy/paste in my svg, because the transformation is always the same, but I cannot say, use transformation named “titi”. Then instead of manually copying some xml, I used m4
The main λ is duplicated 3 times. Each transformation is named by: YTRANSFORMONE, YTRANSFORMTWO and YTRANSFORMTHREE.
+
Each transformation is just a similarity (translate + rotation + scale).
+
Once fixed, we should now simply copy and repeat for each new level.
+
Now it is time to talk about where the magic occurs: YTRANSCOMPLETE. This macro takes two arguments. The current depth and the preceding one. It duplicates using the three transformations the preceding level.
+
+
At level 0 there is only one λ,
+
at level 1 there is 3 λ,
+
at level 2 there is 9 λ
+
etc…
+
+
At the final 5th level there is 35=243 λ. All level combined have 36-1 / 2 = 364 λ.
+
I could preview the final result easily. Without the macro system, I would have to make 5 copy/paste + modifications for each try.
+
Conclusion
+
It was fun to make a fractal in svg, but the interesting part is how to augment the power of a language using this preprocessor method. I used the xslt trick at work for example. I also used it to make include inside obscure format. If all you want is to generate a minimal static website withou using nanoc, jekyll or hakyll (ther are plenty other alternatives). You can consider using m4 to generate your html instead of copy/paste the menu and the footer, or using AJAX.
+
Another usage I thouhgt about is to use m4 to organize languages such as brainfuck.
update (28 march 2015): I now use Haskell LTS instead of a random stackage version.
+
If you are on windows, just download the Haskell Platform and follow the instruction to use Haskell LTS.
+
If you want to know the why and the how; you should read the entire article.
+
+
Why?
+
The main weakness of Haskell as nothing to do with the language itself but with its ecosystem1.
+
The main problem I’ll try to address is the one known as cabal hell. The community is really active in fixing the issue. I am very confident that in less than a year this problem will be one of the past. But to work today, I provide an install method that should reduce greatly two effects of cabal hell:
+
+
dependency error
+
lost time in compilation (poor polar bears)
+
+
With my actual installation method, you should minimize your headache and almost never hit a dependency error. But there could exists some. If you encounter any dependency error, ask gently to the package manager to port its package to stackage.
+
So to install copy/paste the following three lines in your terminal:
You can read the script and you will see that this is quite straightforward.
+
+
It downloads the latest GHC binary for you system and install it.
+
It does the same with the cabal program.
+
It updates your cabal config file to use Haskell LTS.
+
It enable profiling to libraries and executables.
+
It installs some useful binaries that might cause compilation error if not present.
+
+
As the version of libraries is fixed up until you update the Haskell LTS version, you should never use cabal sandbox. That way, you will only compile each needed library once. The compiled objects/binaries will be in your ~/.cabal directory.
+
Some Last Words
+
This script use the latest Haskell LTS. So if you use this script at different dates, the Haskell LTS might have changed.
+
While it comes to cabal hell, some solutions are sandboxes and nix. Unfortunately, sandboxes didn’t worked good enough for me after some time. Furthermore, sandboxes forces you to re-compile everything by project. If you have three yesod projects for example it means a lot of time and CPU. Also, nix didn’t worked as expected on OS X. So fixing the list of package to a stable list of them seems to me the best pragmatic way to handle the problem today.
+
From my point of view, Haskell LTS is the best step in the right direction. The actual cabal hell problem is more a human problem than a tool problem. This is a bias in most programmer to prefer resolve social issues using tools. There is nothing wrong with hackage and cabal. But for a package manager to work in a static typing language as Haskell, packages must work all together. This is a great strength of static typed languages that they ensure that a big part of the API between packages are compatible. But this make the job of package managing far more difficult than in dynamic languages.
+
People tend not to respect the rules in package numbering2. They break their API all the time. So we need a way to organize all of that. And this is precisely what Haskell LTS provide. A set of stable packages working all together. So if a developer break its API, it won’t work anymore in stackage. And whether the developer fix its package or all other packages upgrade their usage. During this time, Haskell LTS end-users will be able to develop without dependency issues.
By ecosystem of a language I mean, the community, the tools, the documentations, the deployment environments, the businesses using the language, etc… Mainly everything that has nothing to do with the detail of a programming language but has to do on how and why we use it.↩
+
I myself am guilty of such behavior. It was a beginner error.↩
Ever been on a website and want to tweet about it? Fortunately, the website might have a button to help you. But do you really know what this button do?
+
The “Like”, “Tweet” and “+1” buttons will call a javascript. It will get access to your cookies. It helps the provider of the button to know who you are.
+
In plain English, the “+1” button will inform Google you are visiting the website, even if you don’t click on “+1”. The same is true for the “like” button for facebook and the “tweet this” button for twitter.
+
The problem is not only a privacy issue. In fact (sadly imho) this isn’t an issue for most people. These button consume computer ressources. Far more than a simple link. It thus slow down a bit the computer and consume energy. These button could also slow down the rendering of your web page.
+
Another aspect is their design. Their look and feel is mostly imposed by the provider.
+
The most problematic aspect in my opinion is to use a third party js on your website. What if tomorrow twitter update their tweet button? If the upgrade break something for only a minority of people, they won’t fix it. This could occur anytime without any notification. They just have to add a document.write in their js you call asynchronously and BAM! Your website is just an empty blank page. And as you call many external ressources, it can be very difficult to find the origin of the problem.
+
Using social network buttons:
+
+
Pros:
+
+
help user share your website,
+
can provide a popularity indicator to your users.
+
+
Cons:
+
+
you help tracking your users,
+
generally doesn’t follow the design of your website,
+
use more computer ressources,
+
slow down your website,
+
executing third party js can break things silently.
+
+
+
Solutions
+
I will provide you two solutions with the following properties:
+
+
Pros:
+
+
help user share your website,
+
doesn’t follow your user,
+
use almost no computer ressource,
+
doesn’t slow down your website,
+
doesn’t execute any third party js on your website.
You use less computer ressources and more generally power ressources which is good for the planet,
+
Your web pages will load faster.
+
+
ps: On my personal website I continue to use Google analytics. Therefore, Google (and only Google, not facebook nor twitter) can track you here. But I might change this in the future.
«There is no reason to wait for browser development to catch up. We can all create better web typography ourselves, today.»
+
+
As somebody who tried to make my website using some nice typography features and in particular ligatures, I believe this is wrong.
+
I already made an automatic system which will detect and replace text by their ligatures in my blog. But this I never published this on the web and this is why.
+
First, what is a ligatures?
+
+
+
+
What is the problem between the Web and ligatures? The first one is: you cannot search them. For example, try to search the word “first”:
The second one is the rendering, for example, try to use a ligature character with small caps:
+
+
first
+
first
+
+
Here is a screenshot of what I see:
+
+
+
+
The browser isn’t able to understand that the ligature character “fi” should render as fi when rendered in small caps. And one part of the problem is you should choose to display a character in small caps using css.
+
This way, how could you use a ligature Unicode character on a site on which you could change the css?
+
Let’s compare to LaTeX.
+
+
+
+
If you take attention to detail, you’ll see the first “first” contains a ligature. Of course the second render nicely. The code I used were:
Clearly fix the rendering of ligature in a browser is a difficult task. Simply imagine the number of strange little exceptions:
+
+
The text is rendered in small caps, I cannot use ligature.
+
The current word contains a ligature unicode character, I should search for ligature in this one.
+
The current font does not defined the ligature unicode character, we shouldn’t use it, etc
+
A javascript command changed the CSS, I should verify if I had to revert the insertion of ligatures characters
+
etc…
+
+
Nonetheless if someone has a solution, I would be happy to hear about it.
+
+
+
+
In fact, you might see a ligature and the search works because I now use some CSS ninja skills: text-rendering: optimizelegibility. But it also works because I use the right font; Computer Modern. Steal my CSS at will.↩
In Learn Vim Progressively I’ve show how Vim is great for editing text, and navigating in the same file (buffer). In this short article you’ll see how I use Vim as an IDE. Mainly by using some great plugins.
+
+
Vim Plugin Manager
+
There are a lot of Vim plugins. To manage them I use vim-plug.
☞ Note I have two parts in my .vimrc. The first part contains the list of all my plugins. The second part contains the personal preferences I setted for each plugin. I’ll separate each part by ... in the code.
+
+
Survival
+
Colorscheme
+
+
+
+
Before anything, you should protect your eyes using a readable and low contrast colorscheme.
+
For this I use solarized dark. To add it, you only have to write this in your ~/.vimrc file:
You should be able to see and destroy trailing whitespaces.
+
+
+
+
Plug 'bronson/vim-trailing-whitespace'
+
You can clean trailing whitespace with :FixWhitespace.
+
And also you should see your 80th column.
+
if (exists('+colorcolumn'))
+ set colorcolumn=80
+ highlight ColorColumn ctermbg=9
+endif
+
+
+
+
File Management
+
One of the most important hidden skills in programming is the ability to search and find files in your projects.
+
The majority of people use something like NERDTree. This is the classical left column with a tree of files of your project. I stopped to use this. And you should probably too.
+
I switched to unite. No left column lost. Faster to find files. Mainly it works like Spotlight on OS X.
+
First install ag (the silver search). If you don’t know ack or ag your life is going to be upgraded. This is a simple but essential tool. It is mostly a grep on steroids.
+
" Unite
+" depend on vimproc
+" ------------- VERY IMPORTANT ------------
+" you have to go to .vim/plugin/vimproc.vim and do a ./make
+" -----------------------------------------
+Plug 'Shougo/vimproc.vim'
+Plug 'Shougo/unite.vim'
+
+...
+
+let g:unite_source_history_yank_enable = 1
+try
+ let g:unite_source_rec_async_command='ag --nocolor --nogroup -g ""'
+ call unite#filters#matcher_default#use(['matcher_fuzzy'])
+catch
+endtry
+" search a file in the filetree
+nnoremap <space><space> :split<cr> :<C-u>Unite -start-insert file_rec/async<cr>
+" reset not it is <C-l> normally
+:nnoremap <space>r <Plug>(unite_restart)
+
Now type space twice. A list of files appears. Start to type some letters of the file you are searching for. Select it, type return and bingo the file opens in a new horizontal split.
+
+
+
+
If something goes wrong just type <space>r to reset the unite cache.
+
Now you are able to search file by name easily and efficiently.
+
Now search text in many files. For this you use ag:
+
Plug 'rking/ag.vim'
+...
+" --- type ° to search the word in all files in the current dir
+nmap ° :Ag <c-r>=expand("<cword>")<cr><cr>
+nnoremap <space>/ :Ag
+
Don’t forget to add a space after the :Ag.
+
These are two of the most powerful shortcut for working in a project. using ° which is nicely positioned on my azerty keyboard. You should use a key close to *.
+
So what ° is doing? It reads the string under the cursor and search for it in all files. Really useful to search where a function is used.
+
If you type <space>/ followed by a string, it will search for all occurrences of this string in the project files.
+
So with this you should already be able to navigate between files very easily.
+
Language Agnostic Plugins
+
Git
+
+
+
+
Show which line changed since your last commit.
+
Plug 'airblade/vim-gitgutter'
+
And the “defacto” git plugin:
+
Plug 'tpope/vim-fugitive'
+
You can reset your changes from the latest git commit with :Gread. You can stage your changes with :Gwrite.
Just select and type Return then space. Type Return many type to change the alignments.
+
If you want to align the second column, Return then 2 then space.
+
+
+
+
Basic auto completion: C-n & C-p
+
Vim has a basic auto completion system. The shortcuts are C-n and C-p while you are in insert mode. This is generally good enough in most cases. For example when I open a file not in my configured languages.
+
Haskell
+
My current Haskell programming environment is great!
+
Each time I save a file, I get a comment pointing to my errors or proposing me how to improve my code.
+
So here we go:
+
+
☞ Don’t forget to install ghc-mod with: cabal install ghc-mod
I use - for my leader because I use , a lot for its native usage.
+
+
-ht will highlight and show the type of the block under the cursor.
+
-hT will insert the type of the current block.
+
-hh will unhighlight the selection.
+
+
+
+
+
Clojure
+
+
+
+
My main language at work is Clojure. And my current vim environment is quite good. I lack the automatic integration to lein-kibit thought. If I have the courage I might do it myself one day. But due to the very long startup time of clojure, I doubt I’ll be able to make a useful vim plugin.
+
So mainly you’ll have real rainbow-parenthesis (the default values are broken for solarized).
+
I used the vim paredit plugin before. But it is too restrictive. Now I use sexp which feel more coherent with the spirit of vim.
Working with Clojure will becomre quite smoother. You can eval any part of your code, you must launch a Clojure REPL manually in another terminal thought.
+
Last words
+
I hope it will be useful.
+
Last but not least, if you want to use my vim configuration you can get it here:
Yesod is a framework which has recently matured to the point where you should consider using it. Before telling you why you should learn Haskell and use Yesod, I will illustrate the many features Yesod introduces which are missing in other frameworks.
When you create a web application, a lot of time is spent dealing with strings. Strings for URL, HTML, JavaScript, CSS, SQL, etc… To prevent malicious usage you have to protect each strings to be sure, no script will pass from one point to another. Suppose a user enter this user name:
You must transform each < into <. Without this transformation alert will appear each time you try to display this user name. Safe types associate with each string what kind of string it is. Is it a string for URL? For javascript? For HTML? And the right protection is made by default to prevent problems.
+
Yesod does its best to handle cross scripting issues. Both between the client and the server and between the server and your DB. Here is an example:
Yesod’s widgets are different from javascript widget. For yesod, widgets are sets of small parts of a web application. If you want to use many widgets in a same page yesod do the work. Some examples of widget are:
+
+
the footer of a webpage,
+
the header of a webpage with a menu,
+
a button which appears only when scrolling down,
+
etc…
+
+
For each of this part, you might need,
+
+
a bit of HTML,
+
a bit of CSS and
+
a bit of javascript.
+
+
Some in the header, some in the body.
+
You can declare a widget as this (note I use a very high meta-language):
Furthermore, if you use say 10 widgets each with a bit of CSS, yesod will create a unique and compressed CSS file. Except if you expressed a need to change the header by using different CSS.
+
This is just awesome!
+
Optimized routing
+
In standard routing system you have for each entry a couple: regexp → handler
+
The only way to discover the right rules is to match each regexp to the current URL. Then you can see behaviour such as, if you change the order of the rules you can lose or win time.
+
On the other hand yesod compiles the routes. Therefore it can optimize it. Of course two routes must not interfere.
Speed. This is just astounding. Look at this and then to this.
+
Haskell. This is certainly hard to learn but also incredibly awesome. If you want to make you a favor. Just learn Haskell. It will be difficult, far more than you can imagine. It is very different from all other languages I used. But it will blow your mind and learn you a bunch of new programming concepts.
+
Good ideas, excellent community. I follow yesod from some month now and the speed at which the project progress is incredible.
+
+
If you are a haskeller, I believe you shouldn’t fear the special syntax imposed by the standard yesod way of doing things. Just try it more than the firsts basic tutorials.
+
Until here I believe it goes in the right direction. Even if I believe the real future is by generating HTML pages from the client (using javascript) and server limited to serve JSON (or XML, or any object representation system).
+
To conclude, Yesod is awesome. Just overcome the difficulties about learning a bit of haskell and try it!
tl;dr: Some hints on how to make great documentation for Haskell libraries.
+
+
Create a Tutorial module containing nothing except documentation.
+
Mention the Tutorial module in your cabal description
+
Use doctest to check your documentation is up to date
+
For more complex real world examples, link to the source of some test.
+
+
+
Great documentation make a big difference. A bad documentation could simply make people not using your lib.
+
My friend was learning Haskell. To start he tried a Haskell library to make a small application. The documentation was deprecated to the point he wasn’t able to make a basic example work. How do you believe he felt? What does he thought about Haskell in general?
+
So here are my hint on how to make a great documentation in Haskell.
+
Documentation can take many different form.
+
+
Tutorials/Guides – write some prose which friendly take a user by hand and help him
+
Examples – how to use each function
+
Generated API Documentation – haddock
+
+
Hints
+
Tutorials/Guides
+
+
Create a new module named Tutorial (or Guide.GuideTopic)
+
Create a link to the tutorial in the cabal description
To prevent obsolescence of your tutorial, use doctest.
+
That way when you’ll do a stack test or cabal test you’ll get errors if some example doesn’t work anymore.
+
Examples (doctest)
+
doctest is a great way to provide examples in your code documentation. These example will then be used as tests. Apparently it comes from Python community.
There are plenty of alternative solution. I provide the one I believe would be used by most people. So if you use github simply create an account on travis.
+
Add a .travis.yml file in your repo containing the content of the file here and remove the builds you don’t need. It will build your project using a lot of different GHC versions and environemnts.
+
If you are afraid by such its complexity you might just want to use this one:
If you didn’t declared your package to stackage, please do it. It isn’t much work. Just edit a file to add your package. And you’ll could be able to add another badge:
data MyData a b
+ = C1 a b -- ^ doc for constructor C1
+ | C2 a b -- ^ doc for constructor C2
+
+data MyData a b
+ = C { a :: TypeA -- ^ field a description
+ , b :: TypeB -- ^ field b description
+ }
+
Module:
+
{-|
+Module : MyModule
+Description: Short description
+Copyright : (c)
+License : MIT
+
+Here is a longer description of this module.
+With some code symbol @MyType@.
+And also a block of code:
+
+@
+data MyData = C Int Int
+
+myFunction :: MyData -> Int
+@
+
+-}
+
Documentation Structure:
+
module MyModule (
+ -- * Classes
+ C(..),
+ -- * Types
+ -- ** A data type
+ T,
+ -- ** A record
+ R,
+ -- * Some functions
+ f, g
+ ) where
+
That will generate headings.
+
Other Random Ideas
+
In Haskell we have great tools like hayoo! and hoogle.
+
And hackage and stackage provide also a lot of informations.
+
But generally we lack a lot of Tutorials and Guides. This post was an attempt to help people making more of them.
+
But there are other good ideas to help improve the situation.
+
Create a doc with link to best practices
+
In clojure when you create a new project using lein new my-project a directory doc is created for you. It contains a file with a link to this blog post:
If you try to search for some clojure function on a search engine there is a big chance the first result will link to:
+
+
clojuredocs.org: try to search for reduce, update-in or index for example
+
+
For each symbol necessiting a documentation. You don’t only have the details and standard documentation. You’ll also get:
+
+
Responsive Design (sometime you want to look at documentation on a mobile)
+
Contributed Examples
+
Contributed See Also section
+
Contributed notes/comments
+
+
clojuredocs.org is an independant website from the official Clojure website.
+
Most of the time, if you google the function you search you end up on clojuredocs for wich there are many contributions.
+
Currently stackage is closer to these feature than hackage. Because on stackage you have access to the README and also some comments by package.
+
I believe it would be more efficient to have at least a page by module and why not a page by symbol (data, functions, typeclasses…).
+
For example, we could provide details about foldl for example. Also as there would be less information to display, it will make the design cleaner.
+
Today, if you want to help documenting, you need to make a PR to the source of some library. While if we had an equivalent to clojuredocs for Haskell, adding documentation would simply be a few clicks away:
+
+
login
+
add/edit some example, comments, see-also section
+
+
There are more than 23k people on /r/haskell. If only 1% of them would take 10 minutes adding a bit of documentation it will certainly change a lot of things in the percieved documentation quality.
+
And last but not least,
+
Design is important
+
+
+
+
Design is a vague word. A good design should care not only about how something look, but also how users will interact with it. For example by removing things to focus on the essential.
+
When I stumble upon some random blog post or random specification in the Haskell community, I had too much a feeling of old fashioned design.
+
If you look at node.js community lot of their web page look cleaner, easier to read and in the end, more user friendly.
+
Haskell is very different from node, I wouldn’t like to replace all long and precise documentation with short human unprecise concepts. I don’t want to transform scientific papers by tweets.
+
But like the scientific community has upgraded with the use of LaTeX, I believe we could find something similar that would make, very clean environment for most of us. A kind of look and feel that will be
+
+
modern
+
device friendly (either on computer, mobile, tablet)
+
efficient, focus on what is most important and is helpful
+
]]>
+
+
+ Vim as IDE
+
+ http://yannesposito.com/Scratch/en/blog/Vim-as-IDE/index.html
+ 2014-12-07T00:00:00Z
+ 2014-12-07T00:00:00Z
+
+
+
+
+
tl;dr: How to use vim as a very efficient IDE
+
In Learn Vim Progressively I’ve show how Vim is great for editing text, and navigating in the same file (buffer). In this short article you’ll see how I use Vim as an IDE. Mainly by using some great plugins.
+
+
Vim Plugin Manager
+
There are a lot of Vim plugins. To manage them I use vim-plug.
☞ Note I have two parts in my .vimrc. The first part contains the list of all my plugins. The second part contains the personal preferences I setted for each plugin. I’ll separate each part by ... in the code.
+
+
Survival
+
Colorscheme
+
+
+
+
Before anything, you should protect your eyes using a readable and low contrast colorscheme.
+
For this I use solarized dark. To add it, you only have to write this in your ~/.vimrc file:
You should be able to see and destroy trailing whitespaces.
+
+
+
+
Plug 'bronson/vim-trailing-whitespace'
+
You can clean trailing whitespace with :FixWhitespace.
+
And also you should see your 80th column.
+
if (exists('+colorcolumn'))
+ set colorcolumn=80
+ highlight ColorColumn ctermbg=9
+endif
+
+
+
+
File Management
+
One of the most important hidden skills in programming is the ability to search and find files in your projects.
+
The majority of people use something like NERDTree. This is the classical left column with a tree of files of your project. I stopped to use this. And you should probably too.
+
I switched to unite. No left column lost. Faster to find files. Mainly it works like Spotlight on OS X.
+
First install ag (the silver search). If you don’t know ack or ag your life is going to be upgraded. This is a simple but essential tool. It is mostly a grep on steroids.
+
" Unite
+" depend on vimproc
+" ------------- VERY IMPORTANT ------------
+" you have to go to .vim/plugin/vimproc.vim and do a ./make
+" -----------------------------------------
+Plug 'Shougo/vimproc.vim'
+Plug 'Shougo/unite.vim'
+
+...
+
+let g:unite_source_history_yank_enable = 1
+try
+ let g:unite_source_rec_async_command='ag --nocolor --nogroup -g ""'
+ call unite#filters#matcher_default#use(['matcher_fuzzy'])
+catch
+endtry
+" search a file in the filetree
+nnoremap <space><space> :split<cr> :<C-u>Unite -start-insert file_rec/async<cr>
+" reset not it is <C-l> normally
+:nnoremap <space>r <Plug>(unite_restart)
+
Now type space twice. A list of files appears. Start to type some letters of the file you are searching for. Select it, type return and bingo the file opens in a new horizontal split.
+
+
+
+
If something goes wrong just type <space>r to reset the unite cache.
+
Now you are able to search file by name easily and efficiently.
+
Now search text in many files. For this you use ag:
+
Plug 'rking/ag.vim'
+...
+" --- type ° to search the word in all files in the current dir
+nmap ° :Ag <c-r>=expand("<cword>")<cr><cr>
+nnoremap <space>/ :Ag
+
Don’t forget to add a space after the :Ag.
+
These are two of the most powerful shortcut for working in a project. using ° which is nicely positioned on my azerty keyboard. You should use a key close to *.
+
So what ° is doing? It reads the string under the cursor and search for it in all files. Really useful to search where a function is used.
+
If you type <space>/ followed by a string, it will search for all occurrences of this string in the project files.
+
So with this you should already be able to navigate between files very easily.
+
Language Agnostic Plugins
+
Git
+
+
+
+
Show which line changed since your last commit.
+
Plug 'airblade/vim-gitgutter'
+
And the “defacto” git plugin:
+
Plug 'tpope/vim-fugitive'
+
You can reset your changes from the latest git commit with :Gread. You can stage your changes with :Gwrite.
Just select and type Return then space. Type Return many type to change the alignments.
+
If you want to align the second column, Return then 2 then space.
+
+
+
+
Basic auto completion: C-n & C-p
+
Vim has a basic auto completion system. The shortcuts are C-n and C-p while you are in insert mode. This is generally good enough in most cases. For example when I open a file not in my configured languages.
+
Haskell
+
My current Haskell programming environment is great!
+
Each time I save a file, I get a comment pointing to my errors or proposing me how to improve my code.
+
So here we go:
+
+
☞ Don’t forget to install ghc-mod with: cabal install ghc-mod
I use - for my leader because I use , a lot for its native usage.
+
+
-ht will highlight and show the type of the block under the cursor.
+
-hT will insert the type of the current block.
+
-hh will unhighlight the selection.
+
+
+
+
+
Clojure
+
+
+
+
My main language at work is Clojure. And my current vim environment is quite good. I lack the automatic integration to lein-kibit thought. If I have the courage I might do it myself one day. But due to the very long startup time of clojure, I doubt I’ll be able to make a useful vim plugin.
+
So mainly you’ll have real rainbow-parenthesis (the default values are broken for solarized).
+
I used the vim paredit plugin before. But it is too restrictive. Now I use sexp which feel more coherent with the spirit of vim.
Working with Clojure will becomre quite smoother. You can eval any part of your code, you must launch a Clojure REPL manually in another terminal thought.
+
Last words
+
I hope it will be useful.
+
Last but not least, if you want to use my vim configuration you can get it here:
update (28 march 2015): I now use Haskell LTS instead of a random stackage version.
+
If you are on windows, just download the Haskell Platform and follow the instruction to use Haskell LTS.
+
If you want to know the why and the how; you should read the entire article.
+
+
Why?
+
The main weakness of Haskell as nothing to do with the language itself but with its ecosystem1.
+
The main problem I’ll try to address is the one known as cabal hell. The community is really active in fixing the issue. I am very confident that in less than a year this problem will be one of the past. But to work today, I provide an install method that should reduce greatly two effects of cabal hell:
+
+
dependency error
+
lost time in compilation (poor polar bears)
+
+
With my actual installation method, you should minimize your headache and almost never hit a dependency error. But there could exists some. If you encounter any dependency error, ask gently to the package manager to port its package to stackage.
+
So to install copy/paste the following three lines in your terminal:
You can read the script and you will see that this is quite straightforward.
+
+
It downloads the latest GHC binary for you system and install it.
+
It does the same with the cabal program.
+
It updates your cabal config file to use Haskell LTS.
+
It enable profiling to libraries and executables.
+
It installs some useful binaries that might cause compilation error if not present.
+
+
As the version of libraries is fixed up until you update the Haskell LTS version, you should never use cabal sandbox. That way, you will only compile each needed library once. The compiled objects/binaries will be in your ~/.cabal directory.
+
Some Last Words
+
This script use the latest Haskell LTS. So if you use this script at different dates, the Haskell LTS might have changed.
+
While it comes to cabal hell, some solutions are sandboxes and nix. Unfortunately, sandboxes didn’t worked good enough for me after some time. Furthermore, sandboxes forces you to re-compile everything by project. If you have three yesod projects for example it means a lot of time and CPU. Also, nix didn’t worked as expected on OS X. So fixing the list of package to a stable list of them seems to me the best pragmatic way to handle the problem today.
+
From my point of view, Haskell LTS is the best step in the right direction. The actual cabal hell problem is more a human problem than a tool problem. This is a bias in most programmer to prefer resolve social issues using tools. There is nothing wrong with hackage and cabal. But for a package manager to work in a static typing language as Haskell, packages must work all together. This is a great strength of static typed languages that they ensure that a big part of the API between packages are compatible. But this make the job of package managing far more difficult than in dynamic languages.
+
People tend not to respect the rules in package numbering2. They break their API all the time. So we need a way to organize all of that. And this is precisely what Haskell LTS provide. A set of stable packages working all together. So if a developer break its API, it won’t work anymore in stackage. And whether the developer fix its package or all other packages upgrade their usage. During this time, Haskell LTS end-users will be able to develop without dependency issues.
By ecosystem of a language I mean, the community, the tools, the documentations, the deployment environments, the businesses using the language, etc… Mainly everything that has nothing to do with the detail of a programming language but has to do on how and why we use it.↩
+
I myself am guilty of such behavior. It was a beginner error.↩
tl;dr: Learn how to start a new Haskell project. Translate a starter tool written in zsh in Haskell using its own result.
+
+
“Good Sir Knight, will you come with me to Camelot, and join us at the Round Table?”
+
+
In order to work properly with Haskell you need to initialize your environment. Typically, you need to use a cabal file, create some test for your code. Both, unit test and propositional testing (random and exhaustive up to a certain depth). You need to use git and generally hosting it on github. Also, it is recommended to use cabal sandboxes. And as bonus, an auto-update tool that recompile and retest on each file save.
+
In this article, we will create such an environment using a zsh script. Then we will write a Haskell project which does the same work as the zsh script. You will then see how to work in such an environment.
+
If you are starting to understand Haskell but consider yourself a beginner, this tutorial will show you how to make a real application using quite surprisingly a lot of features:
+
+
use colorized output
+
interact with a user in command line
+
read/write files
+
kind of parse a file (in fact, simply split it)
+
use a templating system (mustache: fill a data structure, write files)
+
make a HTTP GET request then parse the JSON answer and use it
+
use random
+
create a cabal package
+
add and use non source files to a cabal package
+
Test your code (both unit testing and property testing)
+
+
☞ zsh is by its nature more suitable to file manipulation. But the Haskell code is clearly more organized while quite terse for a multi-purpose language.
+
☞ holy-project is on hackage. It can be installed with cabal update && cabal install holy-project.
While the article is very good, I lacked some minor informations1. Inspired by it, I created a simple script to initialize a new Haskell project. During the process I improved some things a bit.
What does this script do that cabal init doesn’t do?
+
+
Use cabal sandbox
+
It initialize git with the right .gitignore file.
+
Use tasty to organize your tests (HUnit, QuickCheck and SmallCheck).
+
Use -Wall for ghc compilation.
+
Will make references to Holy Grail
+
Search your default github username via github api.
+
+
zsh really?
+
+
+
+
Developing the script in zsh was easy. But considering its size, it is worth to rewrite it in Haskell. Furthermore, it will be a good exercise.
+
Patricide
+
In a first time, we initialize a new Haskell project with holy-haskell.sh:
+
+> ./holy-haskell.sh
+Bridgekeeper: Stop!
+Bridgekeeper: Who would cross the Bridge of Death
+Bridgekeeper: must answer me these questions three,
+Bridgekeeper: ere the other side he see.
+You: Ask me the questions, bridgekeeper, I am not afraid.
+
+Bridgekeeper: What is the name of your project?
+> Holy project
+Bridgekeeper: What is your name? (Yann Esposito (Yogsototh))
+>
+Bridgekeeper: What is your email? (Yann.Esposito@gmail.com)
+>
+Bridgekeeper: What is your github user name? (yogsototh)
+>
+Bridgekeeper: What is your project in less than ten words?
+> Start your Haskell project with cabal, git and tests.
+Initialize git
+Initialized empty Git repository in .../holy-project/.git/
+Create files
+ .gitignore
+ holy-project.cabal
+ Setup.hs
+ LICENSE (MIT)
+ test/Test.hs
+ test/HolyProject/Swallow/Test.hs
+ src/HolyProject/Swallow.hs
+ test/HolyProject/Coconut/Test.hs
+ src/HolyProject/Coconut.hs
+ src/HolyProject.hs
+ src/Main.hs
+Cabal sandboxing, install and test
+...
+ many compilations lines
+...
+Running 1 test suites...
+Test suite Tests: RUNNING...
+Test suite Tests: PASS
+Test suite logged to: dist/test/holy-project-0.1.0.0-Tests.log
+1 of 1 test suites (1 of 1 test cases) passed.
+All Tests
+ Swallow
+ swallow test: OK
+ coconut
+ coconut: OK
+ coconut property: OK
+ 148 tests completed
+
+All 3 tests passed
+
+
+
+Bridgekeeper: What... is the air-speed velocity of an unladen swallow?
+You: What do you mean? An African or European swallow?
+Bridgekeeper: Huh? I... I don't know that.
+[the bridgekeeper is thrown over]
+Bridgekeeper: Auuuuuuuuuuuugh
+Sir Bedevere: How do you know so much about swallows?
+You: Well, you have to know these things when you're a king, you know.
+
+
The different steps are:
+
+
small introduction quotes
+
ask five questions – three question sir…
+
create the directory for the project
+
init git
+
create files
+
sandbox cabal
+
cabal install and test
+
run the test directly in the terminal
+
small goodbye quotes
+
+
Features to note:
+
+
color in the terminal
+
check some rules on the project name
+
random message if error
+
use ~/.gitconfig file in order to provide a default name and email.
+
use the github API which returns JSON to get the default github user name.
+
+
So, apparently nothing too difficult to achieve.
+
We should now have an initialized Haskell environment for us to work. The first thing you should do, is to go into this new directory and launch ‘./auto-update’ in some terminal. I personally use tmux on Linux or the splits in iTerm 2 on Mac OS X. Now, any modification of a source file will relaunch a compilation and a test.
The haskell version is made by hand where zsh already had a capitalize operation on string with many words. Here is the difference between the shell and haskell way (note I splitted the effect of concatMap as map and concat):
In Haskell, while possible, we shouldn’t put the file content in the source code. We have a relatively easy way to include external file in a cabal package. This is what we will be using.
+
Furthermore, we need a templating system to replace small part of the static file by computed values. For this task, I choose to use hastache, a Haskell implementation of Mustache templates3.
+
Add external files in a cabal project
+
Cabal provides a way to add files which are not source files to a package. You simply have to add a Data-Files: entry in the header of the cabal file:
Now we simply have to create our files at the specified path. Here is for example the first lines of the LICENSE file.
+
The MIT License (MIT)
+
+Copyright (c) {{year}}{{author}}
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+...
+
It will be up to our program to replace the {{year}} and {{author}} at runtime. We have to find the files. Cabal will create a module named Paths_holy_project. If we import this module we have the function genDataFileName at our disposal. Now we can read the files at runtime like this:
A first remark is for portability purpose we shouldn’t use String for file path. For example on Windows / isn’t considered as a subdirectory character. To resolve this problem we will use FilePath:
In order to use hastache we can either create a context manually or use generics to create a context from a record. This is the last option we will show here. So in a first time, we need to import some modules and declare a record containing all necessary informations to create our project.
We use external files in mustache format. We ask question to our user to fill a data structure. We use this data structure to create a context. Hastache use this context with the external files to create the project files.
+
Git and Cabal
+
+
+
+
We need to initialize git and cabal. For this we simply call external command with the system function.
We want to retrieve the ~/.gitconfig file content and see if it contains a name and email information. We will need to access to the HOME environment variable. Also, as we use bytestring package for hastache, let’s take advantage of this library.
We could notice, getNameAndMail doesn’t read the full file and stop at the first occurrence of name and mail.
+
Use the github API
+
+
+
+
The task seems relatively easy, but we’ll see there will be some complexity hidden. Make a request on https://api.github.com/search/users?q=<email>. Parse the JSON and get the login field of the first item.
+
So the first problem to handle is to connect an URL. For this we will use the http-conduit package.
But, after some research, I discovered we must declare an User-Agent in the HTTP header to be accepted by the github API. So we have to change the HTTP Header, and our code became slightly more complex:
So now, we have a String containing a JSON representation. In javascript we would have used login=JSON.parse(body).items[0].login. How does Haskell will handle it (knowing the J in JSON is for Javascript)?
+
First we will need to add the lens-aeson package and use it that way:
It looks ugly, but it’s terse. In fact each function (^?), key and nth has some great mathematical properties and everything is type safe. Unfortunately I had to make my own jsonValueToString. I hope I simply missed a simpler existing function.
The HolyProject.hs file contains mostly the code that ask questions, show errors and copy files using hastache.
+
One of the benefits in modularizing the code is that our main code is clearer. Some functions are declared only in a module and are not exported. This help us hide technical details. For example, the modification of the HTTP header to use the github API.
+
Documenting
+
+
+
+
We didn’t take much advantage of the project structure yet. A first thing is to generate some documentation. Before most function I added comment starting with -- |. These comment will be used by haddock to create a documentation. First, you need to install haddock manually.
And magically, you’ll have a documentation in dist/doc/html/holy-project/index.html.
+
Tests
+
While the Haskell static typing is quite efficient to prevent entire classes of bugs, Haskell doesn’t discard the need to test to minimize the number of bugs.
+
Unit Testing with HUnit
+
+
+
+
It is generally said to test we should use unit testing for code in IO and QuickCheck or SmallCheck for pure code.
+
A unit test example on pure code is in the file test/HolyProject/Swallow/Test.hs:
Note swallow is (++). We group tests by group. Each group can contain some test suite. Here we have a test suite with only one test. The (@=?) verify the equality between its two parameters.
+
So now, we could safely delete the directory test/HolyProject/Swallow and the file src/HolyProject/Swallow.hs. And we are ready to make our own real world unit test. We will first test the module HolyProject.GithubAPI. Let’s create a file test/HolyProject/GithubAPI/Test.hs with the following content:
You have to modify your cabal file. More precisely, you have to add HolyProject.GithubAPI in the exposed modules of the library secion). You also have to update the test/Test.hs file to use GithubAPI instead of Swallow.
+
So we have our example of unit testing using IO. We search the github nickname for some people I know and we verify github continue to give the same answer as expected.
+
Property Testing with SmallCheck and QuickCheck
+
+
+
+
When it comes to pure code, a very good method is to use QuickCheck and SmallCheck. SmallCheck will verify all cases up to some depth about some property. While QuickCheck will verify some random cases.
+
As this kind of verification of property is mostly doable on pure code, we will test the StringUtils module.
+
So don’t forget to declare HolyProject.StringUtils in the exposed modules in the library section of your cabal file. Remove all references to the Coconut module.
+
Modify the test/Test.hs to remove all references about Coconut. Create a test/HolyProject/StringUtils/Test.hs file containing:
+All Tests
+ StringUtils
+ SC projectNameFromString idempotent: OK
+ 206 tests completed
+ SC capitalize idempotent: OK
+ 1237 tests completed
+ QC projectNameFromString idempotent: FAIL
+ *** Failed! Falsifiable (after 19 tests and 5 shrinks):
+ "a a"
+ Use --quickcheck-replay '18 913813783 2147483380' to reproduce.
+ GithubAPI
+ Yann: OK
+ Jasper: OK
+
+1 out of 5 tests failed
+
+
The test fail, but this is not an error. Our capitalize function shouldn’t be idempotent. I simply added this test to show what occurs when a test fail. If you want to look more closely to the error you could do this:
It is important to use ./interact instead of ghci. Because we need to tell ghci how to found the package installed.
+
Apparently, SmallCheck didn’t found any counter example. I don’t know how it generates Strings and using deeper search is really long.
+
Conclusion
+
+
+
+
Congratulation!
+
Now you could start programming in Haskell and publish your own cabal package.
+
+
+
+
For example, you have to install the test libraries manually to use cabal test.↩
+
There is no easy way to do something like name=$(ask name). Simply because $(ask name) run in another process which doesn’t get access to the standard input↩
+
Having a good level of power in templates is very difficult. imho Mustache has made the best compromise.↩
tl;dr: Determine with the most objectivity possible the best(s) web framework(s) depending on your needs. Here are the results. Please note the actual ability to take a rational decision is pretty bad for now.
+
+
This is it.
+You’ve got the next big idea.
+You just need to make a very simple web application.
+
It sounds easy! You just need to choose a good modern web framework, when suddenly:
+
+
After your brain stack overflowed, you decide to use a very simple methodology. Answer two questions:
+
Which language am I familiar with?
+What is the most popular web framework for this language?
+
Great! This is it.
+
But, you continually hear this little voice.
+
+
“You didn’t made a bad choice, yes. But …
+you hadn’t made the best either.”
+
+
This article try to determine in the most objective and rational way the best(s) web framework(s) depending on your needs. To reach this goal, I will provide a decision tool in the result section.
+
I will use the following methodology:
+
Methodology
+
+
Model how to make choice
+
+
choose important parameters
+
organize (hierarchize) them
+
write down an objective chooser
+
+
Grab objective quantified informations about web frameworks relatively to choosen parameters
+
Sanitize your data in order to handle imprecisions, lack of informations…
+
Apply the model of choice to your informations
+
+
+
☞ Important Note
+I am far from happy to the actual result. There are a lot of biases, for example in the choice of the parameters. The same can be said about the data I gathered. I am using very imprecise informations. But, as far as I know, this is the only article which use many different parameters to help you choose a web framework.
Here are the important features (properties/parameters) I selected to make the choice:
+
+
Popularity, which correlate with:
+
+
number of tested libraries
+
facility to find learning material
+
ability to find another developer to work with
+
+
Efficiency, which is generally correlated to:
+
+
how much processing power you’ll need per user
+
maintenance price per user
+
how long the user will wait to see/update data
+
+
Expressiveness, which is generally correlated to:
+
+
faster development
+
flexibility, adaptability
+
+
Robustness, which correlate with:
+
+
security
+
fewer bugs
+
+
+
Each feature is quite important and mostly independant from each other. I tried to embrace most important topics concerning web frameworks with these four properties. I am fully concious some people might lack another important feature. Nonetheless the methodology used here can be easily replicated. If you lack an important property add it at will and use this choice method.
+
Also each feature is very hard to measure with precision. This is why we will only focus on order of magnitude.
+
For each property a framework could have one of the six possible values: Excellent, Very Good, Good, Medium, Bad or Very Bad
+
So how to make a decision model from these informations?
+
One of the most versatile method is to give a weight for each cluster value. And to select the framework maximizing this score:
We don’t make any difference between excellent and very good in popularity.
+
Concerning efficient framework in excellent cluster will have 2 more points than the “very good” cluster.
+
+
So for each framework we compute its score relatively to a weighted table. And we select the best(s).
+
Example: Using this hypothetic framework and the preceeding table.
+
+
+
+
+
Expressiveness
+
Popularity
+
Efficiency
+
Robustness
+
+
+
+
+
yog
+
Excellent
+
Very Bad
+
Medium
+
Very Good
+
+
+
+
score(yog) = 10 + 0 + 4 + 8 = 22
+
+
Most needs should be expressed by such a weighted table. In the result section, we will discuss this further.
+
It is now time to try to get these measures.
+
Objective measures
+
None of the four properties I choosen can be measured with perfect precision. But we could get the order of magnitude for each.
+
I tried to focus on the framework only. But it is often easier to start by studying the language first.
+
For example, I have datas about popularity by language and I also have different datas concerning popularity by framework. Even if I use only the framework focused datas in my final decision model, it seemed important to me to discuss about the datas for the languages. The goal is to provide a tool to help decision not to give a decision for you.
+
Popularity
+
RedMonk Programming Language Rankings (January 2013) provide an apparent good measure of popularity. While not perfect the current measure feel mostly right. They create an image using stack overflow and github data. Vertical correspond to the number of questions on stackoverflow. Horizontal correspond to the number of projects on github.
+
If you look at the image, your eye can see about four clusters. The 1st cluster correspond to mainstream languages:
+
+
Most developer know at least one of these language.
+
The second cluster is quite bigger. It seems to correspond to languages with a solid community behind them.
+
+
I don’t get into detail, but you could also see third and fourth tier popular languages.
I don’t thing I could find easily web frameworks for third or fourth tier languages.
+
For now, I only talked about language popularity. But what about framework popularity? I made a test using number of question on stackoverflow only. Then by dividing by two for each 6 cluster:
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
%
+
+
+
+
+
Excellent
+
Ruby
+
Rails
+
176208
+
100%
+
+
+
Very Good
+
Python
+
Django
+
57385
+
<50%
+
+
+
+
Java
+
Servlet
+
54139
+
+
+
+
+
Java
+
Spring
+
31641
+
+
+
+
+
Node.js
+
node.js
+
27243
+
+
+
+
+
PHP
+
Codeigniter
+
21503
+
+
+
+
+
Groovy
+
Grails
+
20222
+
+
+
+
Good
+
Ruby
+
Sinatra
+
8631
+
<25%
+
+
+
+
Python
+
Flask
+
7062
+
+
+
+
+
PHP
+
Laravel
+
6982
+
+
+
+
+
PHP
+
Kohana
+
5959
+
+
+
+
+
Node.js
+
Express
+
5009
+
+
+
+
Medium
+
PHP
+
Cake
+
4554
+
<13%
+
+
+
+
C♯
+
ServiceStack
+
3838
+
+
+
+
+
Scala
+
Play
+
3823
+
+
+
+
+
Java
+
Wicket
+
3819
+
+
+
+
+
Dart
+
Dart
+
3753
+
+
+
+
+
PHP
+
Slim
+
3361
+
+
+
+
+
Python
+
Tornado
+
3321
+
+
+
+
+
Scala
+
Lift
+
2844
+
+
+
+
+
Go
+
Go
+
2689
+
+
+
+
Bad
+
Java
+
Tapestry
+
1197
+
<6%
+
+
+
+
C♯
+
aspnet
+
1000
+
+
+
+
+
Haskell
+
Yesod
+
889
+
+
+
+
+
PHP
+
Silex
+
750
+
+
+
+
+
PHP
+
Lithium
+
732
+
+
+
+
+
C♯
+
nancy
+
705
+
+
+
+
Very bad
+
Java
+
Grizzly
+
622
+
<3%
+
+
+
+
Erlang
+
Cowboy
+
568
+
+
+
+
+
Perl
+
Dancer
+
496
+
+
+
+
+
PHP
+
Symphony2
+
491
+
+
+
+
+
Go
+
Revel
+
459
+
+
+
+
+
Clojure
+
Compojure
+
391
+
+
+
+
+
Perl
+
Mojolicious
+
376
+
+
+
+
+
Scala
+
Scalatra
+
349
+
+
+
+
+
Scala
+
Finagle
+
336
+
+
+
+
+
PHP
+
Phalcon
+
299
+
+
+
+
+
js
+
Ringo
+
299
+
+
+
+
+
Java
+
Gemini
+
276
+
+
+
+
+
Haskell
+
Snap
+
263
+
+
+
+
+
Perl
+
Plack
+
257
+
+
+
+
+
Erlang
+
Elli
+
230
+
+
+
+
+
Java
+
Dropwizard
+
188
+
+
+
+
+
PHP
+
Yaf
+
146
+
+
+
+
+
Java
+
Play1
+
133
+
+
+
+
+
Node.js
+
Hapi
+
131
+
+
+
+
+
Java
+
Vertx
+
60
+
+
+
+
+
Scala
+
Unfiltered
+
42
+
+
+
+
+
C
+
onion
+
18
+
+
+
+
+
Clojure
+
http-kit
+
17
+
+
+
+
+
Perl
+
Kelp
+
16
+
+
+
+
+
PHP
+
Micromvc
+
13
+
+
+
+
+
Lua
+
Openresty
+
8
+
+
+
+
+
C++
+
cpoll-cppsp
+
5
+
+
+
+
+
Clojure
+
Luminus
+
3
+
+
+
+
+
PHP
+
Phreeze
+
1
+
+
+
+
+
As we can see, our framework popularity indicator can be quite different from its language popularity. For now I didn’t found a nice way to merge the results from RedMonk with these one. So I’ll use these unperfect one. Hopefully the order of magninute is mostly correct for most framework.
+
Efficiency
+
Another objective measure is efficiency. We all know benchmarks are all flawed. But they are the only indicators concerning efficiency we have.
+
I used the benchmark from benchmarksgame. Mainly, there are five clusters:
+
+
+
+
1x→2x
+
C, C++
+
+
+
2x→3x
+
Java 7, Scala, OCamL, Haskell, Go, Common LISP
+
+
+
3x→10x
+
C♯, Clojure, Racket, Dart
+
+
+
10x→30x
+
Erlang
+
+
+
30x→
+
PHP, Python, Perl, Ruby, JRuby
+
+
+
+
Remarks concerning some very slow languages:
+
+
PHP ; huge variations, can be about 1.5x C speed in best case.
+
Python ; huge variations, can be about 1.5x C speed in best case
+
Perl ; Can be about 3x C speed in best case
+
Ruby, JRuby ; mostly very slow.
+
+
This is a first approach. The speed of the language for basic benchmarks. But, here we are interrested in web programming. Fortunately techempower has made some tests focused on most web frameworks:
These benchmark doesn’t fit well with our needs. The values are certainly quite imprecise to your real usage. The goal is just to get an order of magnitude for each framework. Another problem is the high number of informations.
+
As always, we should remember these informations are also imprecise. So I simply made some classes of efficiency.
+
Remark: I separated the clusters by using power of 2 relatively to the fastest.
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
slowness
+
+
+
+
+
Excellent
+
C++
+
cpoll-cppsp
+
114,711
+
1×
+
+
+
+
Jav
+
gemini
+
105,204
+
+
+
+
+
Lua
+
openresty
+
93,882
+
+
+
+
+
Jav
+
servlet
+
90,580
+
+
+
+
+
C++
+
cpoll-pool
+
89,167
+
+
+
+
+
Go
+
go
+
76,024
+
+
+
+
+
Sca
+
finagle
+
68,413
+
+
+
+
+
Go
+
revel
+
66,990
+
+
+
+
+
Jav
+
rest-express
+
63,209
+
+
+
+
Very Good
+
Jav
+
wicket
+
48,772
+
>2×
+
+
+
+
Sca
+
scalatra
+
48,594
+
+
+
+
+
Clj
+
http-kit
+
42,703
+
+
+
+
+
Jav
+
spring
+
36,643
+
>3×
+
+
+
+
PHP
+
php
+
36,605
+
+
+
+
+
Jav
+
tapestry
+
35,032
+
+
+
+
+
Clj
+
compojure
+
32,088
+
+
+
+
+
JS
+
ringo
+
31,962
+
+
+
+
+
Jav
+
dropwizard
+
31,514
+
+
+
+
+
Clj
+
luminus
+
30,672
+
+
+
+
Good
+
Sca
+
play-slick
+
29,950
+
>4×
+
+
+
+
Sca
+
unfiltered
+
29,782
+
+
+
+
+
Erl
+
elli
+
28,862
+
+
+
+
+
Jav
+
vertx
+
28,075
+
+
+
+
+
JS
+
nodejs
+
27,598
+
+
+
+
+
Erl
+
cowboy
+
24,669
+
+
+
+
+
C
+
onion
+
23,649
+
+
+
+
+
Hkl
+
yesod
+
23,304
+
+
+
+
+
JS
+
express
+
22,856
+
>5×
+
+
+
+
Sca
+
play-scala
+
22,372
+
+
+
+
+
Jav g
+
rizzly-jersey
+
20,550
+
+
+
+
+
Py
+
tornado
+
20,372
+
>6×
+
+
+
+
PHP
+
phalcon
+
18,481
+
+
+
+
+
Grv
+
grails
+
18,467
+
+
+
+
+
Prl
+
plack
+
16,647
+
>7×
+
+
+
+
PHP
+
yaf
+
14,388
+
+
+
+
Medium
+
JS
+
hapi
+
11,235
+
>10×
+
+
+
+
Jav
+
play1
+
9,979
+
+
+
+
+
Hkl
+
snap
+
9,196
+
+
+
+
+
Prl
+
kelp
+
8,250
+
+
+
+
+
Py
+
flask
+
8,167
+
+
+
+
+
Jav
+
play-java
+
7,905
+
+
+
+
+
Jav p
+
lay-java-jpa
+
7,846
+
+
+
+
+
PHP
+
micromvc
+
7,387
+
+
+
+
+
Prl
+
dancer
+
5,040
+
>20×
+
+
+
+
Prl
+
mojolicious
+
4,371
+
+
+
+
+
JS
+
ringo-conv
+
4,249
+
+
+
+
+
Py
+
django
+
4,026
+
+
+
+
+
PHP
+
codeigniter
+
3,809
+
>30×
+
+
+
Bad
+
Rby
+
rails
+
3,445
+
+
+
+
+
Sca
+
lift
+
3,311
+
+
+
+
+
PHP
+
slim
+
3,112
+
+
+
+
+
PHP
+
kohana
+
2,378
+
>40×
+
+
+
+
PHP
+
silex
+
2,364
+
+
+
+
Very Bad
+
PHP
+
laravel
+
1,639
+
>60×
+
+
+
+
PHP
+
phreeze
+
1,410
+
+
+
+
+
PHP
+
lithium
+
1,410
+
+
+
+
+
PHP
+
fuel
+
1,410
+
+
+
+
+
PHP
+
cake
+
1,287
+
>80×
+
+
+
+
PHP
+
symfony2
+
879
+
>100×
+
+
+
+
C#
+
aspnet-mvc
+
871
+
+
+
+
+
Rby
+
sinatra
+
561
+
>200×
+
+
+
+
C#
+
servicestack
+
51
+
+
+
+
+
Dar
+
dart
+
0
+
+
+
+
+
C#
+
nancy
+
0
+
+
+
+
+
Prl
+
web-simple
+
0
+
+
+
+
+
These are manually made clusters. But you get the idea. Certainly, some framework could jump between two different clusters. So this is something to remember. But as always, the order of magnitude is certainly mostly right.
+
Expressiveness
+
Now, how to objectively measure expressiveness?
+
RedMonk had a very good idea to find an objective (while imprecise) measure of each language expressiveness. Read this article for details.
+
After filtering languages suitable for web development, we end up with some clusters:
+
+
+
+
Cluster
+
Languages
+
+
+
+
+
Excellent
+
Coffeescript, Clojure, Haskell
+
+
+
Very Good
+
Racket, Groovy, R, Scala, OCamL, F♯, Erlang, Lisp, Go
+
+
+
Medium
+
Perl, Python, Objective-C, Scheme, Tcl, Ruby
+
+
+
Bad
+
Lua, Fortran (free-format), PHP, Java, C++, C♯
+
+
+
Very Bad
+
Assembly, C, Javascript,
+
+
+
+
Unfortunately there is no information about dart. So I simply give a very fast look at the syntax. As it looked a lot like javascript and js is quite low. I decided to put it close to java.
+
Also an important remark, javascript score very badly here while coffeescript (compiling to js) score “excellent”. So if you intend to use a javascript framework but only with coffescript that should change substantially the score. As I don’t believe it is the standard. Javascript oriented framework score very badly regarding expressiveness.
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Grv
+
grails
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Medium
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Bad
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
Very Bad
+
C
+
onion
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
+
Robustness
+
I couldn’t find any complete study to give the number of bug relatively to each framework/language.
+
But one thing I saw from experience is the more powerful the type system the safest your application is. While the type system doesn’t remove completely the need to test your application a very good type system tend to remove complete classes of bug.
+
Typically, not using pointer help to reduce the number of bugs due to bad references. Also, using a garbage collector, reduce greatly the probability to access unallocated space.
+
+
From my point of view, robustness is mostly identical to safety.
+
Here are the clusters:
+
+
+
+
Excellent
+
Haskell, Scheme, Erlang
+
+
+
Very Good
+
Scala, Java, Clojure
+
+
+
Good
+
Ruby, Python, Groovy, javascript, PHP
+
+
+
Medium
+
C++, C#, Perl, Objective-C, Go, C
+
+
+
+
So applying this to frameworks gives the following clusters:
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Good
+
Grv
+
grails
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Medium
+
C
+
onion
+
+
+
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
+
The result
+
For the result I initialized the table with my own needs.
+
And I am quite happy it confirms my current choice. I sware I didn’t given yesod any bonus point. I tried to be the most objective and factual as possible.
+
Now, it is up to you to enter your preferences.
+
On each line you could change how important a feature is for you. From essential to unsignificant. Of course you could change the matrix at will.
+
I just show a top 10 frameworks. In order to give a more understandable measure I provide the log of the score.
+
+
+
+
+
+Excellent
+
+
+Very good
+
+
+Good
+
+
+Medium
+
+
+Bad
+
+
+Very bad
+
+
+Importance
+
+
+
+
+Expressiveness
+
+
+
+
+Popularity
+
+
+
+
+Efficiency
+
+
+
+
+Robustness
+
+
+
+
+Click to force refresh
+
+
+
+
+
+
+
I didn’t had the courage in explaining in what the scoring system is good. Mostly, if you use product instead of sum for the score you could use power of e for the values in the matrix. And you could see the matrix as a probability matrix (each line sum to 1). Which provide a slighly better intuition on whats going on.
+
Remember only that values are exponential. Do not double an already big value for example the effect would be extreme.
+
Conclusion
+
All of this is based as most as I could on objective data. The choice method seems both rather rational and classical. It is now up to you to edit the score matrix to set your needs.
+
I know that in the current state there are many flaws. But it is a first system to help make a choice rationally.
+
I encourage you to go further if you are not satisfied by my method.
+
The source code for the matrix shouldn’t be too hard to read. Just read the source of this webpage. You could change the positionning of some frameworks if you believe I made some mistake by placing them in some bad clusters.
+
So I hope this tool will help you in making your life easier.
Hakyll can be considered as a minimal cms. But more generally it is a library helping file generation. We can view it as an advanced build system (like make).
+
From the user perspective I blog this way:
+
+
I open an editor (vim in my case) and edit a markdown file. It looks like this
I open a browser and reload time to time to see the change.
+
Once I finished I’ve written a very minimal script which mainly do a git push. My blog is hosted on github.
+
+
Being short sighted one could reduce the role of Hakyll to:
+
+
create (resp. update) html file when I create (resp. change) a markdown file.
+
+
While it sounds easy, there are a lot of hidden details:
+
+
Add metadatas like keywords.
+
Create an archive page containing a list of all the posts.
+
Deal with static files.
+
Creating an rss feed.
+
Filter the content with some function.
+
Dealing with dependencies.
+
+
The work of Hakyll is to help you with these. But let’s start with the basic concepts.
+
The concepts and syntax
+
+
+
+
For each file you create, you have to provide:
+
+
a destination path
+
a list of content filters.
+
+
First, let’s start with the simplest case: static files (images, fonts, etc…). Generally, you have a source directory (here is the current directory) and a destination directory _site.
This program will copy static/foo.jpg to _site/static/foo.jpg. I concede this is a bit overkill for a simple cp. Now how to write a markdown file and generate an html one?
But horror! _site/posts/cthulhu.html is not a complete html file. It doesn’t have any header nor footer, etc… This is where you use templates. I simply add a new directive in the compile block.
As Sir Robin said just before dying before the Bridge of Death:
+
+
“That’s EASY!”
+
– Sir Robin, the Not-Quite-So-Brave-As-Sir-Lancelot
+
+
Real customization
+
Now that we understand the basic functionality. How to:
+
+
use SASS?
+
add keywords?
+
simplify url?
+
create an archive page?
+
create an rss feed?
+
filter the content?
+
add abbreviations support?
+
manage two languages?
+
+
Use SASS
+
That’s easy. Simply call the executable using unixFilter. Of course you’ll have to install SASS (gem install sass). And we also use compressCss to gain some space.
Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in 2013-03-20-My-New-Post.md.
+
I migrated from an older blog and didn’t want to change my url. Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata published. And the solution is here:
And base.html is a standard template (simpler than post.html).
+
archiveCtx provide a context containing an html representation of a list of posts in the metadata named posts. It will be used in the templates/archive.html file with $posts$.
postList returns an html representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is post-item.html:
generate partially rendered posts (no css, js, etc…)
+
+
We could then render the posts twice. One for html rendering and another time for rss. Remark we need to generate the rss version to create the html one.
+
One of the great feature of Hakyll is to be able to save snapshots. Here is how:
Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed don’t need a source markdown file. Then we create a new file from no one. Instead of using match, we use create:
Great idea certainly steal from nanoc (my previous blog engine)!
+
Filter the content
+
As I just said, nanoc was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a lot of filters in nanoc. I lost some because I don’t use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type String -> String.
+
Also we generally want prefilters (to filter the markdown) and postfilters (to filter the html after the pandoc compilation).
It will search for all string starting by ‘%’ and it will search in the Map if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.
Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.
The full code is here. And except from the main file, I use literate Haskell. This way the code should be easier to understand.
+
If you want to know why I switched from nanoc:
+
My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.
+
So I had to do something about it. I had two choices:
+
+
Correct my old code (in Ruby)
+
Duplicate the core functionalities with Hakyll (in Haskell)
+
+
I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.
+
So far I am very happy with the switch. A complete build is about 4x faster. I didn’t broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.
+
I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:
We could also add the metadatas in an external file (foo.md.metadata).↩
+
+]]>
+
+
+ Social link the right way
+
+ http://yannesposito.com/Scratch/en/blog/Social-link-the-right-way/index.html
+ 2013-03-14T00:00:00Z
+ 2013-03-14T00:00:00Z
+
+
+
+
+
tl;dr: Default share buttons track your users, aren’t uniform with your design, use computer ressources and slow down your web page loading.
+
Do it right. Use static links instead.
+
If you don’t want to read, just copy/paste this in your html:
Ever been on a website and want to tweet about it? Fortunately, the website might have a button to help you. But do you really know what this button do?
+
The “Like”, “Tweet” and “+1” buttons will call a javascript. It will get access to your cookies. It helps the provider of the button to know who you are.
+
In plain English, the “+1” button will inform Google you are visiting the website, even if you don’t click on “+1”. The same is true for the “like” button for facebook and the “tweet this” button for twitter.
+
The problem is not only a privacy issue. In fact (sadly imho) this isn’t an issue for most people. These button consume computer ressources. Far more than a simple link. It thus slow down a bit the computer and consume energy. These button could also slow down the rendering of your web page.
+
Another aspect is their design. Their look and feel is mostly imposed by the provider.
+
The most problematic aspect in my opinion is to use a third party js on your website. What if tomorrow twitter update their tweet button? If the upgrade break something for only a minority of people, they won’t fix it. This could occur anytime without any notification. They just have to add a document.write in their js you call asynchronously and BAM! Your website is just an empty blank page. And as you call many external ressources, it can be very difficult to find the origin of the problem.
+
Using social network buttons:
+
+
Pros:
+
+
help user share your website,
+
can provide a popularity indicator to your users.
+
+
Cons:
+
+
you help tracking your users,
+
generally doesn’t follow the design of your website,
+
use more computer ressources,
+
slow down your website,
+
executing third party js can break things silently.
+
+
+
Solutions
+
I will provide you two solutions with the following properties:
+
+
Pros:
+
+
help user share your website,
+
doesn’t follow your user,
+
use almost no computer ressource,
+
doesn’t slow down your website,
+
doesn’t execute any third party js on your website.
You use less computer ressources and more generally power ressources which is good for the planet,
+
Your web pages will load faster.
+
+
ps: On my personal website I continue to use Google analytics. Therefore, Google (and only Google, not facebook nor twitter) can track you here. But I might change this in the future.
Sometimes, the type determine a lot about the function★:
+
fst :: (a,b) -> a -- Only one choice
+snd :: (a,b) -> b -- Only one choice
+f :: a -> [a] -- Many choices
+-- Possibilities: f x=[], or [x], or [x,x] or [x,...,x]
+
+? :: [a] -> [a] -- Many choices
+-- can only rearrange: duplicate/remove/reorder elements
+-- for example: the type of addOne isn't [a] -> [a]
+addOne l = map (+1) l
+-- The (+1) force 'a' to be a Num.
The couple (F,fmap) is a \(\Hask\)'s functor if for any x :: F a:
+
fmap id x = x
+
fmap (f.g) x= (fmap f . fmap g) x
+
+
+
+
Haskell Functors Example: Maybe
+
+
data Maybe a = Just a | Nothing
+instance Functor Maybe where
+ fmap :: (a -> b) -> (Maybe a -> Maybe b)
+ fmap f (Just a) = Just (f a)
+ fmap f Nothing = Nothing
+
fmap (+1) (Just 1) == Just 2
+fmap (+1) Nothing == Nothing
+fmap head (Just [1,2,3]) == Just 1
+
+
+
Haskell Functors Example: List
+
+
instance Functor ([]) where
+ fmap :: (a -> b) -> [a] -> [b]
+ fmap = map
abusing notations denoting join by ⊙; this is equivalent to (F ⊙ F) ⊙ F = F ⊙ (F ⊙ F)
+
There exists η :: a -> F a s.t. η⊙F=F=F⊙η
+
+
+
+
Klesli composition
+
Now the composition works as expected. In Haskell ◎ is <=< in Control.Monad.
+
g <=< f = \x -> join ((fmap g) (f x))
+
f x = [x] ⇒ f 1 = [1] ⇒ (f <=< f) 1 = [1] ✓
+g x = [x+1] ⇒ g 1 = [2] ⇒ (g <=< g) 1 = [3] ✓
+h x = [x+1,x*3] ⇒ h 1 = [2,3] ⇒ (h <=< h) 1 = [3,6,4,9] ✓
+
+
+
+
We reinvented Monads!
+
A monad is a triplet (M,⊙,η) where
+
+
\(M\) an Endofunctor (to type a associate M a)
+
\(⊙:M×M→M\) a nat. trans. (i.e. ⊙::M (M a) → M a ; join)
+
\(η:I→M\) a nat. trans. (\(I\) identity functor ; η::a → M a)
+
+
Satisfying
+
+
\(M ⊙ (M ⊙ M) = (M ⊙ M) ⊙ M\)
+
\(η ⊙ M = M = M ⊙ η\)
+
+
+
+
Compare with Monoid
+
A Monoid is a triplet \((E,∙,e)\) s.t.
+
+
\(E\) a set
+
\(∙:E×E→E\)
+
\(e:1→E\)
+
+
Satisfying
+
+
\(x∙(y∙z) = (x∙y)∙z, ∀x,y,z∈E\)
+
\(e∙x = x = x∙e, ∀x∈E\)
+
+
+
+
Monads are just Monoids
+
+
A Monad is just a monoid in the category of endofunctors, what's the problem?
+
+
The real sentence was:
+
+
All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
+
+
+
+
Example: List
+
+
[] :: * -> * an Endofunctor
+
\(⊙:M×M→M\) a nat. trans. (join :: M (M a) -> M a)
+
\(η:I→M\) a nat. trans.
+
+
-- In Haskell ⊙ is "join" in "Control.Monad"
+join :: [[a]] -> [a]
+join = concat
+
+-- In Haskell the "return" function (unfortunate name)
+η :: a -> [a]
+η x = [x]
A LOT of monad tutorial on the net. Just one example; the State Monad
+
DrawScene to State Screen DrawScene ; still pure.
+
main = drawImage (width,height)
+
+drawImage :: Screen -> DrawScene
+drawImage screen = do
+ drawPoint p screen
+ drawCircle c screen
+ drawRectangle r screen
+
+drawPoint point screen = ...
+drawCircle circle screen = ...
+drawRectangle rectangle screen = ...
+
main = do
+ put (Screen 1024 768)
+ drawImage
+
+drawImage :: State Screen DrawScene
+drawImage = do
+ drawPoint p
+ drawCircle c
+ drawRectangle r
+
+drawPoint :: Point ->
+ State Screen DrawScene
+drawPoint p = do
+ Screen width height <- get
+ ...
+
+
+
fold
+
+
+
+
κατα-morphism
+
+
+
+
κατα-morphism: fold generalization
+
acc type of the "accumulator": fold :: (acc -> a -> acc) -> acc -> [a] -> acc
Algebra representing the (+1) and also knowing about the 0.
+
+
First example, make length on [Char]
+
+
+
κατα-morphism: Type work
+
+data StrF a = Cons Char a | Nil
+data Str' = StrF Str'
+
+-- generalize the construction of Str to other datatype
+-- Mu: type fixed point
+-- Mu :: (* -> *) -> *
+
+data Mu f = InF { outF :: f (Mu f) }
+data Str = Mu StrF
+
+-- Example
+foo=InF { outF = Cons 'f'
+ (InF { outF = Cons 'o'
+ (InF { outF = Cons 'o'
+ (InF { outF = Nil })})})}
+
+
+
+
κατα-morphism: missing information retrieved
+
type Algebra f a = f a -> a
+instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+
cata :: Functor f => Algebra f a -> Mu f -> a
+cata f = f . fmap (cata f) . outF
+
+
+
+
κατα-morphism: Finally length
+
All needed information for making length.
+
instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+length' :: Str -> Int
+length' = cata phi where
+ phi :: Algebra StrF Int -- StrF Int -> Int
+ phi (Cons a b) = 1 + b
+ phi Nil = 0
+
+main = do
+ l <- length' $ stringToStr "Toto"
+ ...
+
+
+
κατα-morphism: extension to Trees
+
Once you get the trick, it is easy to extent to most Functor.
+
type Tree = Mu TreeF
+data TreeF x = Node Int [x]
+
+instance Functor TreeF where
+ fmap f (Node e xs) = Node e (fmap f xs)
+
+depth = cata phi where
+ phi :: Algebra TreeF Int -- TreeF Int -> Int
+ phi (Node x sons) = 1 + foldr max 0 sons
+
+
+
Conclusion
+
Category Theory oriented Programming:
+
+
Focus on the type and operators
+
Extreme generalisation
+
Better modularity
+
Better control through properties of types
+
+
No cat were harmed in the making of this presentation.
tl;dr: My short and highly subjective feelings about the programming languages I used.
+
+
BASIC
+
+
+
+
The language of my firsts programs! I was about 10, with an MO5 and Amstrad CPC 6128 and even with my Atari STe. This is the language of GOTOs. Ô nostalgia. Unfortunately this might be the only interesting part of this language.
+
Today this language is obsolescent. It is not even good to learn programming. I know some compiler exists now. But this is not enough to try to learn it.
I was about 10 when I played with logo to draw things on a screen.
+
I remember the Bach’s music while the program loaded.
+
At that time we had to load the program into the memory using tapes. This one was a rare one. It didn’t made an awfull ‘Krrrkrr cssssss krrr’ noise.
+
Some years after, I used it to learn programming to my college student. It was a really good first language. Making fractals is like a game for children.
I made my firsts real serious program with Pascal. I must confess I find it inferior to C. I made graph algorithms, sort algorithms even some IA (genetic) algorithms. In the end I prefer C.
+
C
+
+
+
+
The pointer’s language.
+
Le programming language.
+
Once you understand loops and recursion, it is time to do serious things. If you want to make good quality code, knowing C is almost mandatory.
+
This language is close to the machine language. So much, there is (mostly) a linear relation between the size of your code and the size of the compiled one.
+
In short, each time you write a C instruction there won’t be anything strange that will occurs, like starting a long algorithm behind the scene.
+
While close to the machine, C keep sufficient abstractions to be fun.
+
I made a lot of program with it. From sort algorithms to AI ones (SAT3), system, network programming, etc… It is a very useful language that will help you understand how things works on your computer. Most modern computer language hide a lot of informations on what occurs. This is not the case with C.
+
ADA
+
The “super clean” one.
+
I liked ADA. I must confess I didn’t used it a lot. May be one day I will try it again. I was impressed by asynchronous programming with it. What you need to know is this old language had certainly inspired most new object oriented languages.
+
Object Oriented Languages
+
Until here I just described imperative languages without any object notion.
+
More clearly, the language didn’t helped you to structure your program.
+
In order to limit the number of bugs, particularly for huge programs, we started to think about how to organize computer programs. In the end, from the imperative language culture, it produced the Object Oriented programming (OOP). Beware, the Object Oriented programming isn’t a miracle. Proof? How many bug free software do you use? Furthermore, OOP doesn’t fit all problems. But to make a bank application, an application which help to manage stock, clients or text archives or more generally an information system, the OOP is not so bad.
+
Then Object Oriented Languages appeared everywhere.
+
C++
+
+
+
+
The ugly
+
Industry wanted an Object Oriented Language without losing all their old C code. Solution, keep C and add an Object layer on it. My main concern about C++ is: it does too many things. I appreciated multiple inheritance and templates. In reality I liked a lot C++ while I was working alone. I used it to write DEES my main thesis software. My only concern was about a lack in the STL. In the doc, one could use String<T>. But in reality, T had to be only char or char16. Then I had to reduce my alphabet to 216 letters. Except for some application, the alphabet must be far larger than that. To conclude, I’d say, C++ is very good if you work alone or with a fixed subset of its features.
+
Eiffel
+
+
+
+
Yes, it is a really nice language. Full object in mind. Far cleaner than C++. But it isn’t so popular. Behind C++ there is a large community to help new users and to write libraries. Furthermore, I preferred working with C++. When I learned Eiffel, I programmed a lot with C and liked its syntax.
+
Java
+
+
+
+
The first time I heard about Java it was le Grail!
+
Perfect portability, your program will work on all platform. The language helps limit mistakes, and force you to use good programming habits. But…
+
But It is extremely verbose. Many limitations are quite boring if you know what you’re doing.
+
For example, there is no multiple inheritance. Generally it is a coherent choice when there are a way to compensate. In Java, there are interfaces for this. Except, interfaces can only add methods to a class. You cannot add any attribute to a class except by subclassing. I really lacked this feature.
+
I made a GUI using Java Swing and I created my own notification system between different element of the GUI. In the beginning I only needed to send notification one to one. After some time, I wanted to send one to many notifications. I had to make a bunch of copy/paste inside all my subclasses! Copy/paste are exactly what should be avoided the most by object oriented languages.
+
Another thing: threads. I was forced to make my own thread management system to avoid locks and send notifications between threads (wait the end of this thread, …). At that time I used Java 1.5. This problem should have been solved with Java 1.6. I wish it is the case, but lacking such an essential feature for a language was very bad.
+
In the same idea, it was very long to wait for the “foreach” loops.
+
After my experience, I don’t recommend Java. Portability does not worth this price.
+
GUI portability means mediocre experience on all platforms. Any system it might be (wxWidget, QT, etc…).
+
The Java ideology is “closed”. But it resolve a big problem. It helps medium to low quality developers to work in team without the ability to make too much harm to the product. A good programmer will be able to make very interesting things with it thought. Please note I didn’t say Java programmer are bad programmer.
Even if Objective-C is a relatively low level language. Its dynamic typing ability make it very good for GUI programming. I recommend to continue working with this language. In the end you’ll certainly find it better than expected.
+
Modern Scripting Languages
+
PHP
+
+
+
+
This small script language that we used all to make our website in the time of animated gifs.
+
Nice but no more. Apparently there were a lot of progress since PHP5. Maybe one day I’ll use it again. But behind it, this language has a “script kiddies only” reputation. Also long history of easy to make security holes.
+
In reality PHP is just behind C for the abstraction level. Therefore it has a lot of organisation problems and make it easier to create bugs. For web applications it is a real problem.
+
PHP remains for me the SQL injection language. I made a bit of PHP not so long ago, and it was a pain to protect my application to SQL injection. Yep, I didn’t found any standard library to make this, but I didn’t searched a lot.
+
Python
+
+
+
+
Revelation!
+
When you were used to work with compiled languages (C++, Java) and you start learning Python, it’s like a punch in the face. Programming like it always should have been. Everything is natural, it’s magic. Yes, as good as this. But something so good must have some drawback.
+
And yes, like all interpreted languages, Python is slow. Beware, no just a bit slow like 2 or 3 times slower than C. (like Java for example). No, really slow, about 10 to 20 times slower than C. Argh… Note it is completely usable for many things.
+
Awk
+
If you have to “filter” some files and the filter is not too complicated awk is the ideal language to do this. For example, if you want to know which words in a text file are most used. I used it to modify hundreds of XML files in an easier manner than XSLT.
+
Perl
+
Perl is magic, but the syntax is so hideous nobody can like to work in an environment with many different person in Perl. Or at least, all other collaborators must be excellent programmers. A great feature of perl is its integration with regular expression in its syntax:
This program will replace every toto by titi inside the $var variable. The Perl code is often very compact and generally unreadable. But it is a language good to know. It is a kind of awk under steroids.
+
Ruby
+
Ruby is a very good language. It is often compared (opposed ?) to Python. There are the regular expression operators Perl inside the langage. But the syntax is extremely clear, like in Python. Many feature were inspired by functional programming (as in Python). I used it a lot. It is the worst language I know in term of efficiency. This is the language that lose almost all benchmarks. But it is the perfect tool for prototypes. If you want to make a website prototype, RoR (Ruby on Rails) is certainly one of the best system known to mankind. From idea to realisation, few time will occur. Make this site work for thousands of people, will, on the other hand, certainly require a lot of optimisations.
+
One of the greatest Ruby feature is its ability to make the program extremely readable. It is very close to natural language. On the other hand, I found the Object Oriented layer a bit disappointing. The fact there is no real “class variable” but only “tree class variable” for example.
+
Considering the community, the ruby one feels closer to the creative than the engineer. I am under the impression designer tends to use Ruby instead of Python.
+
Javascript
+
It is the good surprise. During years, javascript was considered as an annoying web experience language. In reality, javascript has many really good qualities. Particularly, it is easy to pass a function in parameter and to create anonymous functions. Recently, javascript became far faster than before and many frameworks and libraries appears:
+
+
Cappuccino, Objective-J (as in objective-C but with javascript)
+
Sproutcore
+
Spine.js
+
Backbone.js
+
jQuery
+
prototype.js
+
+
Particularly with jQuery we can chain functions. It is very nice to use. As I said, this is a good surprise. Javascript was chosen by chance as the script inside your navigator. Instead of the java inspired syntax, everything else is very good. In order to compensate the syntax, you can use CoffeeScript.
+
Functional Languages
+
CamL
+
I learned CamL during the college. It was really interesting. Functional programming is very different to imperative programming (most of popular languages). I had good mathematic intuitions to use this language. But I must confess I never used it for something serious.
+
Haskell
+
I believe I will still learning this language in many years. I must say it is a pleasure. Generally it takes me no more than some hours to some days to learn a new programming language. Concerning Haskell, this is very different. To master Haskell you need to understand very abstract concepts. Monads and Arrows are some of them. I didn’t understood them before I read some scientific paper. Many week will be necessary to master it perfectly (if someone does). Also the community is very friendly and nice. There is no “LOL! URAN00B! RTFM!” And no concession has been made to make this language more popular (I’m looking at you C++, Java and Javascript). This langage remain pure (I know there are two meaning).
+
Concerning making real product with Haskell. In fact, Haskell is very efficient concerning code change. Refactoring code is incredibly easy with Haskell. And in the same time, the Haskell type system helps to make your product bug free.
+
Technically this language is close to perfection. But it has some major problems:
+
+
not so popular
+
hard to learn
+
I also believe there is not actually enough success stories with Haskell
Metapost is a language to program drawings. What make metapost very good? It contains a linear solver. This is really useful to draw things. For example if you write:
This second example, will place the point X at the intersection of the two segments AB and CD.
+
This feature is very helpful, and not only to draw things. Most programming language should think about adding it.
+
zsh
+
Yes, zsh is a shell. But it is also a script language very well suited to file management. For now, it is the best shell I used. I prefer zsh to bash.
+
Prolog
+
I never made something serious with Prolog, but I really loved to use and learn it. I had the chance to learn Prolog with Alain Colmerauer himself. This language try to resolve constraints as much as it can. It has a magic feeling when you use it. We only write constraints, we never put order. A bit like functional programming but far more powerful.
+
Languages to discover
+
Many languages and framework remains to be learnt and tried. Actually I believe I will stay a while with Haskell. Maybe tomorrow I will look at LISP, Scala or Erlang. I also certainly look at clojure to make web application.
+
Tell me if you have any other experience with these programming languages. Of course, my feelings are highly subjectives. But I used all of these languages.
+
[STL]: Standard Tempate Library [GUI]: Graphic User Interface
It is an easy way to aggregate all the update made on websites you like in a single place. You’ll never have to surf many website to see if there is news on a website.
+
choose an aggregator
+
To begin, you must choose an RSS client called aggregator. There is many online clients: website which will present the news in a fancy way.
+
Personally I use Netvibes. I tried a bunch, and it is by far the best - In My Humble Opinion.
+
Of course Google has a client named Google Reader. It is great for content you never want to forgot some article. It is not really useful when you have to handle flux generating many news per day.
+
Subscribe to some website news
+
Once you have chosen your aggregator, you only have to subscribe to websites you like. Do do this, you only have to click on the RSS icon in the top bar of your navigator. Or generally a nice icon is shown into the page you’re reading.
+
Get the news
+
Now, you’ll only have to use your RSS client. And you’ll see all news on all subscribed websites. There is no more need to surf many websites. All news which interest you are in the same place.
YClock is a nice clock screensaver. It has three themes: white, black and red. It is based on a QuartzComposition and with some little Objective-C code to handle gently the frame per second.
One word to explain why there is some validation errors.
+
I wanted to use box-shadows and border-radius.
+
Then here I prefered pragmatism against dogmatism.
+
Using these properties broke my validation but work really well in the two most recent and main browsers (Safari 4 and Firefox 3.5 at the time I’m writing these lines). If you don’t use these browser the page is correctly displayed but not with all the ‘eyecandy’ effects.
+
I believed in the benefits of CSS validation, this is why there is alway the CSS validation link on my pages. And all CSS validate except for properities beginning by -moz and -webkit.
+
+
diff --git a/src/Scratch/files/YAquaBubbles.dmg b/src/Scratch/files/YAquaBubbles.dmg
new file mode 100644
index 0000000..67ba05b
Binary files /dev/null and b/src/Scratch/files/YAquaBubbles.dmg differ
diff --git a/src/Scratch/files/YClock.dmg b/src/Scratch/files/YClock.dmg
new file mode 100644
index 0000000..3ca0ac4
Binary files /dev/null and b/src/Scratch/files/YClock.dmg differ
diff --git a/src/Scratch/files/YPassword-1.6.zip b/src/Scratch/files/YPassword-1.6.zip
new file mode 100644
index 0000000..769002d
Binary files /dev/null and b/src/Scratch/files/YPassword-1.6.zip differ
diff --git a/src/Scratch/files/YPassword-1.7.zip b/src/Scratch/files/YPassword-1.7.zip
new file mode 100644
index 0000000..ad3039e
Binary files /dev/null and b/src/Scratch/files/YPassword-1.7.zip differ
diff --git a/src/Scratch/files/YPassword-1.8.zip b/src/Scratch/files/YPassword-1.8.zip
new file mode 100644
index 0000000..097e8dd
Binary files /dev/null and b/src/Scratch/files/YPassword-1.8.zip differ
diff --git a/src/Scratch/files/cv.pdf b/src/Scratch/files/cv.pdf
new file mode 100644
index 0000000..8a789aa
Binary files /dev/null and b/src/Scratch/files/cv.pdf differ
diff --git a/src/Scratch/files/cv_en.pdf b/src/Scratch/files/cv_en.pdf
new file mode 100644
index 0000000..8a789aa
Binary files /dev/null and b/src/Scratch/files/cv_en.pdf differ
diff --git a/src/Scratch/files/forcePaste.app.zip b/src/Scratch/files/forcePaste.app.zip
new file mode 100644
index 0000000..8cd3cc3
Binary files /dev/null and b/src/Scratch/files/forcePaste.app.zip differ
diff --git a/src/Scratch/files/resume/beamerthemesolarized-dark.sty b/src/Scratch/files/resume/beamerthemesolarized-dark.sty
new file mode 100644
index 0000000..a8ce42a
--- /dev/null
+++ b/src/Scratch/files/resume/beamerthemesolarized-dark.sty
@@ -0,0 +1,156 @@
+% Beamer Color Theme using the Solarized Palette,
+% http://ethanschoonover.com/solarized.
+%
+% Copyright 2012 Jeffrey B. Arnold
+%
+% This program is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% This program is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+
+\ProvidesPackage{beamercolorthemesolarized}[2013/10/11 1.0.1 Solarized color theme for beamer]
+\RequirePackage{etoolbox}
+\RequirePackage{kvoptions}
+
+%% This is ugly. First time using options and conditionals in LaTeX
+\SetupKeyvalOptions{
+ family=solarized,
+ prefix=solarized@,
+}
+\DeclareBoolOption[false]{dark}
+\DeclareComplementaryOption{light}{dark}
+\DeclareStringOption[yellow]{accent}[yellow]
+\ProcessKeyvalOptions*
+
+% Solarized palette
+\definecolor{solarizedBase3}{HTML}{002B36}
+\definecolor{solarizedBase2}{HTML}{073642}
+\definecolor{solarizedBase1}{HTML}{586e75}
+\definecolor{solarizedBase0}{HTML}{657b83}
+\definecolor{solarizedBase00}{HTML}{839496}
+\definecolor{solarizedBase01}{HTML}{93a1a1}
+\definecolor{solarizedBase02}{HTML}{EEE8D5}
+\definecolor{solarizedBase03}{HTML}{FDF6E3}
+\definecolor{solarizedYellow}{HTML}{B58900}
+\definecolor{solarizedOrange}{HTML}{CB4B16}
+\definecolor{solarizedRed}{HTML}{DC322F}
+\definecolor{solarizedMagenta}{HTML}{D33682}
+\definecolor{solarizedViolet}{HTML}{6C71C4}
+\definecolor{solarizedBlue}{HTML}{268BD2}
+\definecolor{solarizedCyan}{HTML}{2AA198}
+\definecolor{solarizedGreen}{HTML}{859900}
+
+% Set Accent color
+% Ugly. Should be done with a switch
+\ifdefstring{\solarized@accent}{yellow}{
+ \colorlet{solarizedAccent}{solarizedYellow}
+}{}
+\ifdefstring{\solarized@accent}{orange}{
+ \colorlet{solarizedAccent}{solarizedOrange}
+}{}
+\ifdefstring{\solarized@accent}{red}{
+ \colorlet{solarizedAccent}{solarizedRed}
+}{}
+\ifdefstring{\solarized@accent}{magenta}{
+ \colorlet{solarizedAccent}{solarizedMagenta}
+}{}
+\ifdefstring{\solarized@accent}{violet}{
+ \colorlet{solarizedAccent}{solarizedViolet}
+}{}
+\ifdefstring{\solarized@accent}{blue}{
+ \colorlet{solarizedAccent}{solarizedBlue}
+}{}
+\ifdefstring{\solarized@accent}{cyan}{
+ \colorlet{solarizedAccent}{solarizedCyan}
+}{}
+\ifdefstring{\solarized@accent}{green}{
+ \colorlet{solarizedAccent}{solarizedGreen}
+}{}
+
+%% Set base colors for dark or light versions
+%% Dark
+% Switch between light and dark themes using the method in the CSS
+% stylesheet http://ethanschoonover.com/solarized
+\ifboolexpe{ bool {solarized@dark}}{
+ \colorlet{solarizedRebase03}{solarizedBase03}
+ \colorlet{solarizedRebase02}{solarizedBase02}
+ \colorlet{solarizedRebase01}{solarizedBase01}
+ \colorlet{solarizedRebase00}{solarizedBase00}
+ \colorlet{solarizedRebase0}{solarizedBase0}
+ \colorlet{solarizedRebase1}{solarizedBase1}
+ \colorlet{solarizedRebase2}{solarizedBase2}
+ \colorlet{solarizedRebase3}{solarizedBase3}
+}{
+ %% Light
+ \colorlet{solarizedRebase03}{solarizedBase3}
+ \colorlet{solarizedRebase02}{solarizedBase2}
+ \colorlet{solarizedRebase01}{solarizedBase1}
+ \colorlet{solarizedRebase00}{solarizedBase0}
+ \colorlet{solarizedRebase0}{solarizedBase00}
+ \colorlet{solarizedRebase1}{solarizedBase01}
+ \colorlet{solarizedRebase2}{solarizedBase02}
+ \colorlet{solarizedRebase3}{solarizedBase03}
+}
+
+\mode
+
+\setbeamercolor{normal text}{fg=solarizedRebase0, bg=solarizedRebase03}
+\setbeamercolor{alerted text}{fg=solarizedAccent}
+% based css pre element
+\setbeamercolor{example text}{fg=solarizedRebase1, bg=solarizedRebase02}
+
+% Header and footer from CSS
+\setbeamercolor{footline}{bg=solarizedRebase02,fg=solarizedRebase01}
+\setbeamercolor{headline}{bg=solarizedRebase01,fg=solarizedRebase1}
+
+% Titles
+\setbeamercolor*{titlelike}{fg=solarizedAccent}
+\setbeamercolor*{frametitle}{fg=solarizedAccent}
+\setbeamercolor*{title}{fg=solarizedAccent}
+
+% Structure elements use css style for header
+\setbeamercolor*{structure}{bg=solarizedRebase01, fg=solarizedRebase1}
+
+% Do not mess with subtle colors in palette. I don't like it.
+\setbeamercolor*{palette primary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette secondary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette tertiary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette quaternary}{bg=solarizedRebase01, fg=solarizedRebase1}
+
+% Make Blocks slightly lighter/darker
+\setbeamercolor{block title}{fg=solarizedAccent, bg=solarizedRebase02}
+%\setbeamercolor{block title alerted}{}
+%\setbeamercolor{block title example}{}
+
+\setbeamercolor{block body}{parent=normal text, bg=solarizedRebase02}
+% \setbeamercolor{block body alerted}{}
+% \setbeamercolor{block body example}{}
+
+% same as footline
+% Set Sidebar and footline to use the css style for footer
+\setbeamercolor*{sidebar}{parent=headline}
+\setbeamercolor*{palette sidebar primary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar secondary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar tertiary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar quaternary}{fg=solarizedRebase01, fg=solarizedRebase1}
+
+% border-color for headings
+\setbeamercolor{separation line}{fg=solarizedRebase0}
+\setbeamercolor{fine separation line}{fg=solarizedRebase0}
+
+\setbeamercolor*{section in sidebar shaded}{parent=palette sidebar primary}
+% a.hover.navlink in CSS
+\setbeamercolor*{section in sidebar}{parent=palette sidebar primary, fg=solarizedRebase02}
+\setbeamercolor*{subsection in sidebar}{parent=section in sidebar}
+\setbeamercolor*{subsection in sidebar shaded}{parent=section in sidebar shaded}
+
+\mode
+
diff --git a/src/Scratch/files/resume/beamerthemesolarized.sty b/src/Scratch/files/resume/beamerthemesolarized.sty
new file mode 100644
index 0000000..9c21d34
--- /dev/null
+++ b/src/Scratch/files/resume/beamerthemesolarized.sty
@@ -0,0 +1,156 @@
+% Beamer Color Theme using the Solarized Palette,
+% http://ethanschoonover.com/solarized.
+%
+% Copyright 2012 Jeffrey B. Arnold
+%
+% This program is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% This program is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see .
+
+\ProvidesPackage{beamercolorthemesolarized}[2013/10/11 1.0.1 Solarized color theme for beamer]
+\RequirePackage{etoolbox}
+\RequirePackage{kvoptions}
+
+%% This is ugly. First time using options and conditionals in LaTeX
+\SetupKeyvalOptions{
+ family=solarized,
+ prefix=solarized@,
+}
+\DeclareBoolOption[false]{dark}
+\DeclareComplementaryOption{light}{dark}
+\DeclareStringOption[yellow]{accent}[yellow]
+\ProcessKeyvalOptions*
+
+% Solarized palette
+\definecolor{solarizedBase03}{HTML}{002B36}
+\definecolor{solarizedBase02}{HTML}{073642}
+\definecolor{solarizedBase01}{HTML}{586e75}
+\definecolor{solarizedBase00}{HTML}{657b83}
+\definecolor{solarizedBase0}{HTML}{839496}
+\definecolor{solarizedBase1}{HTML}{93a1a1}
+\definecolor{solarizedBase2}{HTML}{EEE8D5}
+\definecolor{solarizedBase3}{HTML}{FDF6E3}
+\definecolor{solarizedYellow}{HTML}{B58900}
+\definecolor{solarizedOrange}{HTML}{CB4B16}
+\definecolor{solarizedRed}{HTML}{DC322F}
+\definecolor{solarizedMagenta}{HTML}{D33682}
+\definecolor{solarizedViolet}{HTML}{6C71C4}
+\definecolor{solarizedBlue}{HTML}{268BD2}
+\definecolor{solarizedCyan}{HTML}{2AA198}
+\definecolor{solarizedGreen}{HTML}{859900}
+
+% Set Accent color
+% Ugly. Should be done with a switch
+\ifdefstring{\solarized@accent}{yellow}{
+ \colorlet{solarizedAccent}{solarizedYellow}
+}{}
+\ifdefstring{\solarized@accent}{orange}{
+ \colorlet{solarizedAccent}{solarizedOrange}
+}{}
+\ifdefstring{\solarized@accent}{red}{
+ \colorlet{solarizedAccent}{solarizedRed}
+}{}
+\ifdefstring{\solarized@accent}{magenta}{
+ \colorlet{solarizedAccent}{solarizedMagenta}
+}{}
+\ifdefstring{\solarized@accent}{violet}{
+ \colorlet{solarizedAccent}{solarizedViolet}
+}{}
+\ifdefstring{\solarized@accent}{blue}{
+ \colorlet{solarizedAccent}{solarizedBlue}
+}{}
+\ifdefstring{\solarized@accent}{cyan}{
+ \colorlet{solarizedAccent}{solarizedCyan}
+}{}
+\ifdefstring{\solarized@accent}{green}{
+ \colorlet{solarizedAccent}{solarizedGreen}
+}{}
+
+%% Set base colors for dark or light versions
+%% Dark
+% Switch between light and dark themes using the method in the CSS
+% stylesheet http://ethanschoonover.com/solarized
+\ifboolexpe{ bool {solarized@dark}}{
+ \colorlet{solarizedRebase03}{solarizedBase03}
+ \colorlet{solarizedRebase02}{solarizedBase02}
+ \colorlet{solarizedRebase01}{solarizedBase01}
+ \colorlet{solarizedRebase00}{solarizedBase00}
+ \colorlet{solarizedRebase0}{solarizedBase0}
+ \colorlet{solarizedRebase1}{solarizedBase1}
+ \colorlet{solarizedRebase2}{solarizedBase2}
+ \colorlet{solarizedRebase3}{solarizedBase3}
+}{
+ %% Light
+ \colorlet{solarizedRebase03}{solarizedBase3}
+ \colorlet{solarizedRebase02}{solarizedBase2}
+ \colorlet{solarizedRebase01}{solarizedBase1}
+ \colorlet{solarizedRebase00}{solarizedBase0}
+ \colorlet{solarizedRebase0}{solarizedBase00}
+ \colorlet{solarizedRebase1}{solarizedBase01}
+ \colorlet{solarizedRebase2}{solarizedBase02}
+ \colorlet{solarizedRebase3}{solarizedBase03}
+}
+
+\mode
+
+\setbeamercolor{normal text}{fg=solarizedRebase0, bg=solarizedRebase03}
+\setbeamercolor{alerted text}{fg=solarizedAccent}
+% based css pre element
+\setbeamercolor{example text}{fg=solarizedRebase1, bg=solarizedRebase02}
+
+% Header and footer from CSS
+\setbeamercolor{footline}{bg=solarizedRebase02,fg=solarizedRebase01}
+\setbeamercolor{headline}{bg=solarizedRebase01,fg=solarizedRebase1}
+
+% Titles
+\setbeamercolor*{titlelike}{fg=solarizedAccent}
+\setbeamercolor*{frametitle}{fg=solarizedAccent}
+\setbeamercolor*{title}{fg=solarizedAccent}
+
+% Structure elements use css style for header
+\setbeamercolor*{structure}{bg=solarizedRebase01, fg=solarizedRebase1}
+
+% Do not mess with subtle colors in palette. I don't like it.
+\setbeamercolor*{palette primary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette secondary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette tertiary}{bg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette quaternary}{bg=solarizedRebase01, fg=solarizedRebase1}
+
+% Make Blocks slightly lighter/darker
+\setbeamercolor{block title}{fg=solarizedAccent, bg=solarizedRebase02}
+%\setbeamercolor{block title alerted}{}
+%\setbeamercolor{block title example}{}
+
+\setbeamercolor{block body}{parent=normal text, bg=solarizedRebase02}
+% \setbeamercolor{block body alerted}{}
+% \setbeamercolor{block body example}{}
+
+% same as footline
+% Set Sidebar and footline to use the css style for footer
+\setbeamercolor*{sidebar}{parent=headline}
+\setbeamercolor*{palette sidebar primary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar secondary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar tertiary}{fg=solarizedRebase01, fg=solarizedRebase1}
+\setbeamercolor*{palette sidebar quaternary}{fg=solarizedRebase01, fg=solarizedRebase1}
+
+% border-color for headings
+\setbeamercolor{separation line}{fg=solarizedRebase0}
+\setbeamercolor{fine separation line}{fg=solarizedRebase0}
+
+\setbeamercolor*{section in sidebar shaded}{parent=palette sidebar primary}
+% a.hover.navlink in CSS
+\setbeamercolor*{section in sidebar}{parent=palette sidebar primary, fg=solarizedRebase02}
+\setbeamercolor*{subsection in sidebar}{parent=section in sidebar}
+\setbeamercolor*{subsection in sidebar shaded}{parent=section in sidebar shaded}
+
+\mode
+
diff --git a/src/Scratch/files/resume/resume.beamer.pdf b/src/Scratch/files/resume/resume.beamer.pdf
new file mode 100644
index 0000000..fb85b05
Binary files /dev/null and b/src/Scratch/files/resume/resume.beamer.pdf differ
diff --git a/src/Scratch/files/resume/resume.html b/src/Scratch/files/resume/resume.html
new file mode 100644
index 0000000..8bf9ca4
--- /dev/null
+++ b/src/Scratch/files/resume/resume.html
@@ -0,0 +1,607 @@
+
+
+
+
+
+
+
+
+ Resume
+
+
+
+
+
+
+
Clojure Software Engineer for Cisco (Threatgrid), Remote
+
+
+
2013
+
→
+
2016
+
Machine Learning Scientist & Software Engineer at Vigiglobe, Sophia Antipolis, France
+
+
+
2010
+
→
+
+
Co-Founder of GridPocket, Sophia Antipolis, France
+
+
+
2007
+
→
+
2013
+
AirFrance, Sophia Antipolis, France
+
+
+
10/2006
+
→
+
3/2007
+
Post Ph.D., Hubert Curien Laboratory, St-Etienne, France
+
+
+
10/2004
+
→
+
9/2006
+
ATER (College Degree Teach & Research), Marseille, France
+
+
+
10/2001
+
→
+
9/2004
+
University Monitor (College Degree Teach & Research), Marseille, France
+
+
+
1995
+
→
+
2000
+
Miscellaneous summer jobs
+
+
+
+
Education
+
+
+
+
2004
+
CS Ph.D. in Machine Learning at Université de Provence
+
+
+
2001
+
D.E.A. (Equivalent to Master in Computer science)
+
+
+
2000
+
Maîtrise in Computer Science
+
+
+
1999
+
Licence in Computer Science
+
+
+
1998
+
DEUG MIAS (Math)
+
+
+
1995
+
BAC S (Math)
+
+
+
+
Research Activies: Publications
+
+
+
+
International Journal
+
[Fundamenta Informaticæ, 2008]
+
+
+
+
[Pattern Recognition, 2004]
+
+
+
Internation Conferences
+
[ECML 2008] [ICGI 2006] [COLT 2006]
+
+
+
+
[COLT 2004] [ICALP 2003] [ICGI 2002]
+
+
+
National Journal
+
[JEDAI 2002]
+
+
+
National Conferences
+
[CAp’06] [CAp’04] [CAp’03]
+
+
+
+
+
Presentation
+
I am French with a Post Ph.D in Machine Learning1. Furthermore I love web programming and design.
+
I am currently working remotely for Cisco Security team as a Clojure Software Engineer.
+
Previously I worked for Vigiglobe. The first six months I worked with node.js (API/MongoDB/Web). Then we upgraded our stack to Clojure, Haskell, Mesos, Kafka, Druid, etc… At that time we were two to make all technical decisions. In the end we made a real time analytics of social media content on a scalable architecture. Actually our architecture is able to manage (Aggregation & Machine Learning) thousands of messages per second.2 In particular, I’ve written an Haskell twitter stream absorber able to handle thousands of tweets per seconds. And I coded myself a real time sentiment analysis module taking algebraic properties into account to optimize its efficiency.
+
+
During my Ph.D. I made a C++ program (github3 and resume4). I coded most of standard HMM learning algorithms. I developed an algorithm which I invented during my Ph.D. which use some operational optimization algorithm. During this period I published articles in international conferences and I taught Computer Science to college students.
+
At the Hubert Curien Laboratory I made my post Ph.D. I developed a scientific application in Java/applet/JWS that should be used by biologists. The code has been updated a bit since my 6 month post Ph.D5.
+
I worked in the web industry for Airfrance. My work environment was quite heterogeneous. From shell scripting to manage huge amount of data, web design and production environment.
+
I worked for GridPocket (I am a co-founder). This is a French startup specialized in Electric Grid. I created a private6 web application.
+
I’ve also written an iOS application to manage passwords7.
+
I am the author of some quite popular blog posts8.
+
+
For an almost exhaustive list of my projects, you could check my github account: github.com/yogsototh
Vigiglobe architecture able to analyze thousands of social media messages in realtime. In particular, real time Machine Learning & Statistics.
+
YPassword iOS application
+
Gridpocket web services (from conception to realization, works in correlation with a mobile app)
+
DEES: a 10.000 line C++ command line program. This program implement most HMM standard algorithms & inference algorithms.
+
SeDiL: a Java application using Swing UI. The goal is to provide biologist an easy way to use an algorithm that generate Similarity Matrices for strings but also for Tree structures. Most graphics was done by me, including the drawing of trees. I didn’t used a library for that purpose.
+
For YPassword ; a Dashboard Widget, a web interface, a command line tool.
Web Application used for private team usage at AirFrance 2008 → This application is just done for teh lulz. Not related to the Airfrance work. But still pleasant. Javascript(Prototype.js, Scriptaculous), CSS, PHP/MySQL, Google Talk
+
+
+
+
+
diff --git a/src/Scratch/files/resume/resume.md b/src/Scratch/files/resume/resume.md
new file mode 100644
index 0000000..23636ab
--- /dev/null
+++ b/src/Scratch/files/resume/resume.md
@@ -0,0 +1,361 @@
+---
+title: Resume
+author: Yann Esposito
+abstract: Yann Esposito's Resume
+theme: brutalist
+highlight-style: solarized-dark
+date: 26 July 2016
+---
+
+
+\newpage
+
+# Yann Esposito
+
+\iffalse
+
+[PDF Version](./resume.pdf)
+
+\fi
+
+-------- ---------------------------------------------------------------
+name Yann Esposito
+mail
+port (+33)650845271
+address Bât 9, Résidence Saint Marc
+ 591, avenue Jean Aicard
+ 06700, Saint Laurent du Var
+-------- ---------------------------------------------------------------
+
+## Professional Background
+
+--------- ------ -------- -----------------------------------------------------------------------------------------
+ _2016_ → Clojure Software Engineer for Cisco (Threatgrid), _Remote_
+
+ _2013_ → _2016_ Machine Learning Scientist & Software Engineer at Vigiglobe,
+ _Sophia Antipolis, France_
+
+ _2010_ → Co-Founder of GridPocket, _Sophia Antipolis, France_
+
+ _2007_ → _2013_ AirFrance, _Sophia Antipolis, France_
+
+_10/2006_ → _3/2007_ Post Ph.D., Hubert Curien Laboratory, _St-Etienne, France_
+
+_10/2004_ → _9/2006_ ATER (College Degree Teach _&_ Research), _Marseille, France_
+
+_10/2001_ → _9/2004_ University Monitor (College Degree Teach _&_ Research),
+ _Marseille, France_
+
+ _1995_ → _2000_ Miscellaneous summer jobs
+--------- ------ -------- -----------------------------------------------------------------------------------------
+
+## Education
+
+----- ---------------------------------------------
+_2004_ CS Ph.D. in Machine Learning at Université de Provence
+_2001_ D.E.A. (Equivalent to Master in Computer science)
+_2000_ Maîtrise in Computer Science
+_1999_ Licence in Computer Science
+_1998_ DEUG MIAS (Math)
+_1995_ BAC S (Math)
+----- ---------------------------------------------
+
+## Research Activies: Publications
+
+-------------------------- ---------------------------------------------
+_International Journal_ [Fundamenta Informaticæ, 2008]
+ [Pattern Recognition, 2004]
+_Internation Conferences_ [ECML 2008] [ICGI 2006] [COLT 2006]
+ [COLT 2004] [ICALP 2003] [ICGI 2002]
+_National Journal_ [JEDAI 2002]
+_National Conferences_ [CAp'06] [CAp'04] [CAp'03]
+-------------------------- ---------------------------------------------
+
+\newpage
+
+# Presentation
+
+I am French with a Post Ph.D in Machine Learning[^10].
+Furthermore I love web programming and design.
+
+I am currently working remotely for Cisco Security team
+as a Clojure Software Engineer.
+
+Previously I worked for Vigiglobe.
+The first six months I worked with `node.js` (API/MongoDB/Web).
+Then we upgraded our stack to _Clojure, Haskell, Mesos, Kafka, Druid_, etc...
+At that time we were two to make all technical decisions.
+In the end we made a real time analytics of social media content on a scalable architecture.
+Actually our architecture is able to manage (Aggregation & Machine Learning) thousands of messages per second.[^7]
+In particular, I've written an Haskell twitter stream absorber able to handle thousands of tweets per seconds. And I coded myself a real time sentiment analysis module taking algebraic properties into account to optimize its efficiency.
+
+- During my Ph.D. I made a C++ program (github[^1] and resume[^2]).
+ I coded most of standard HMM learning algorithms.
+ I developed an algorithm which I invented during my Ph.D. which use some operational optimization algorithm.
+ During this period I published articles in international conferences and
+ I taught Computer Science to college students.
+
+- At the Hubert Curien Laboratory I made my post Ph.D.
+ I developed a scientific application in Java/applet/JWS that should be used by biologists.
+ The code has been updated a bit since my 6 month post Ph.D[^3].
+
+- I worked in the web industry for Airfrance.
+ My work environment was quite heterogeneous.
+ From shell scripting to manage huge amount of data, web design
+ and production environment.
+
+- I worked for GridPocket (I am a co-founder).
+ This is a French startup specialized in Electric Grid.
+ I created a private[^6] web application.
+
+- I've also written an iOS application to manage passwords[^4].
+
+- I am the author of some quite popular blog posts[^5].
+
+For an almost exhaustive list of my projects, you could check my
+github account: [github.com/yogsototh](https://github.com/yogsototh)
+
+[^10]: To be more precise in Grammatical Inference
+[^1]:
+[^2]:
+[^3]: (I like to believe I became a better designer ☺)
+[^4]:
+[^5]:
+[^6]: Sorry the code is private I can't show it :(.
+[^7]:
+
+\newpage
+
+# Public things done
+
+- [Cisco (threatgrid)](http://cisco.com) Security & Threat Management.
+- [Vigiglobe](http://vigiglobe.com) architecture able to analyze thousands of social media messages in realtime. In particular, real time Machine Learning & Statistics.
+- [Gridpocket](http://gridpocket.com) web services (from conception to realization, works in correlation with a mobile app)
+- [DEES](https://github.com/yogsototh/DEES): a 10.000 line C++ command line program. This program implement most [HMM](http://en.wikipedia.org/wiki/Hidden_Markov_model) standard algorithms _&_ inference algorithms.
+- [SeDiL](http://labh-curien.univ-st-etienne.fr/SEDiL/): a Java application using Swing UI. The goal is to provide biologist an easy way to use an algorithm that generate Similarity Matrices for strings but also for Tree structures. Most graphics was done by me, including the drawing of trees. I didn't used a library for that purpose.
+- YPassword iOS application
+- [YPassword](http://yannesposito/YPassword) web interface in elm
+- Also relative to YPassword ; a Dashboard Widget, a command line tool.
+- Some websites: [yannesposito.com](http://yannesposito.com)
+- Written a thesis in Machine Learning and published in major international conferences:
+ [ICALP 2003], [COLT 2004] _&_ [COLT 2006].
+- A full javascript web application which display Electric consumption in real time.
+- [mkdocs](http://yogsototh.github.io/mkdocs) (the engine I use to create this document. I exported it in HTML, PDF (using \LaTeX) and SVG.
+- some Mac OS X screensaver, a MetaPost plugin to draw Automata, an RFC-like document to help my student to make a TOR like network, etc...
+- a bunch of other projects see [http://github.com/yogsototh](http://github.com/yogsototh)
+
+\newpage
+
+# Technical Competences
+
+------------------ -----------------------------------------------------------------
+Languages __Haskell__, __Clojure__, __Javascript__,
+ scheme, C, camL, C++, Ruby, Perl, Java, Python, PHP
+Web frontend __elm__, __Clojurescript__, __Reagent__, __Angular.js__, __sass__, etc...
+Web frameworks __compojure-api__, __Yesod__, __servant__, actionhero
+ML Tools __weka__, SVMlight
+Stream Computing __kafka__, __druid__, storm (with clojure)
+UNIX Shell scripts (zsh, bash), awk, \LaTeX, ConTeXt, metapost
+VCS __git__, Bazaar (DCVS), subversion (svn), CVS
+Mac/iOS __Objective-C Cocoa (Mac & iOS)__, Dahsboard widget,
+ Quartz Composer
+------------------ -----------------------------------------------------------------
+
+\newpage
+
+# Jobs
+
+## Clojure Software Engineer for Cisco _2016 →_
+
+- _Remote_
+
+------- --------------------------
+Product Security Threat Management
+Role Clojure Software Engineer
+------- --------------------------
+
+## Machine Learning Scientist _&_ Software Engineer for Vigiglobe _2013 → 2016_
+
+- _Sophia Antipolis, France_
+
+---------- -------------------------------------------------
+Product Scalable Real Time Social Media Analytics
+ Sentiment Analysis
+ Many client side web applications (Angular.js & reagent)
+Role Machine Learning Scientist
+ (fast sentiment analysis, learning protocols, etc..)
+ Full stack engineer (backend to frontend architecture)
+Keywords Clojure, Haskell, node.js, reagent, Angular.js, Stream computing
+---------- -------------------------------------------------
+
+## Co-Founder _&_ freelance for GridPocket _2010 →_
+
+_Sophia Antipolis, France_
+
+---------- -------------------------------------------------
+Product Two API server (one for client, another for administration)
+ A private client side web application
+ An iPhone Application
+ Some Linux boxes to send data to the servers
+ A Linux driver
+Role Full technical responsibilities
+Keywords Ruby, REST, JSON, HTML, CSS, Javascript, AJAX,
+ jQuery, Objective-C, ASIHTTPRequest, CorePlot, CoreData, C
+---------- -------------------------------------------------
+
+## Consultant, AirFrance _2007 →_
+
+_Sophia Antipolis, France_
+
+---------- -------------------------------------------------
+Role In charge of the Airfrance CMS for their website.
+Keywords TeamSite, Perl, XML, XHTML, CSS, javascript, JSP,
+ Unix (Solaris/Linux), Bazaar
+---------- -------------------------------------------------
+
+## Post Ph.D _10/2006 → 3/2007_
+
+_Université Jean Monet, Laboratoire Hubert Curien, Saint-Etienne_
+
+
+---------- -------------------------------------------------
+Product [SeDiL](http://labh-curien.univ-st-etienne.fr/SEDiL/)
+Role Java Developer
+Research Similarity measure between strings or XML trees
+Contact [Marc Sebban](mailto://marc.sebban@univ-st-etienne.fr)
+Keywords UML, Java 1.5, Swing, Java 2D, Java Web Start, Applet,
+ subversion, XML, XHTML, PHP
+---------- -------------------------------------------------
+
+Details:
+
+> Java application: _11 000 lines with javadoc_
+>
+> Main functionalities
+>
+> - learn edit matrices
+> - compute edit distances between trees or strings
+> - visualize trees or sequences (JAVA 2D)
+> - classification using K means
+> - Generate random tree couple from an edit distance matrice
+>
+> Web: [http://labh-curien.univ-st-etienne.fr/SEDiL/](http://labh-curien.univ-st-etienne.fr/SEDiL/)
+
+
+## ATER _10/2004 → 9/2006_
+
+Research _&_ Teacher, Université de Provence, Marseille
+
+_teach 1/2, research 1/6, C++ development 1/3_
+
+DEES ; a C++ software
+
+> _7500 lines of C++ code, 10.000 with comments_
+>
+> Main functionalities:
+>
+> - Mulitiplicity Automata, HMM _&_ PDA Inference,
+> - Baum Welch _&_ Viterbi Algorithms,
+> - GraphViz export,
+> - String Generation from many Models,
+>
+> ------------ ------------------------------
+> Languages C++
+> API STL
+> Environment Linux (Debian) _&_ Windows XP
+> ------------ ------------------------------
+
+## Moniteur des Universités _10/2001 → 9/2004_
+
+Université de Provence, Marseille
+
+_teach 1/3, research 1/3, C++ Development 1/3_
+
+Creation of DEES (see preceeding entry).
+
+\newpage
+
+# Diploma
+
+------ -----------------------------------------------------
+_2004_ Ph.D. degree in Machine Learning
+_2001_ D.E.A. in Computer Science (equivalent to master)
+_2000_ Maîtrise d’Informatique
+_1999_ Licence in Computer Science
+_1998_ DEUG MIAS (math)
+_1995_ BAC S (math)
+------ -----------------------------------------------------
+
+\newpage
+
+# Scientific Publications
+
+## International
+
+-------------- ----------------------------------------------------
+Journals [Fundamenta Inforamticæ vol.86 2008]
+ [Pattern Recognition, 2004]
+
+Conferences [ECML 2008] [COLT 2006] [ICGI 2006]
+ [COLT 2004] [ICALP 2003] [ICGI 2002]
+
+Workshop [TAGI05]
+-------------- ----------------------------------------------------
+
+## National (French)
+
+-------------- ----------------------------------------------------
+Journals [JEDAI, 2003]
+Conferences [CAP 2006] [CAP 2004] [CAP 2003]
+Thesis [Université de Provence 2004]
+-------------- ----------------------------------------------------
+
+\newpage
+
+# Projects
+
+Most of my latest programming activities are publicly available at [github.com/yogsototh](http://github.com/yogsototh)
+
+## Haskell libraries
+
+- [Link to list of packages](http://hackage.haskell.org/user/yogsototh)
+
+- `holy-project`
+- `human-readable-duration`
+- `wai-middleware-caching-lru`
+- `wai-middleware-caching-redis`
+- `wai-middleware-caching`
+
+## YPassword _2008 →_
+
+Mainly an iOS application:
+
+- [YPassword, `http://ypassword.espozito.com`](http://ypassword.espozito.com)
+
+I've done fully the website from scratch. Also there are some javascript implementation of YPassword method:
+
+> - a Mac OS X dashboard widget,
+> - a Cappuccino Web application,
+> - a jQuery Web application,
+> - a command line tool,
+> - an Applescript helper
+
+## Anonymous Network Project _02/2006 → 06/2006_
+
+Made a protocol similar to [TOR](http://www.torproject.org) for student.
+
+## Other projects
+
+- Web Application used for private team usage at AirFrance _2008 →_
+ This application is just done _[for teh lulz](http://cache.ohinternet.com/images/thumb/f/fa/4tehlulz.jpg/618px-4tehlulz.jpg)_.
+ Not related to the Airfrance work. But still pleasant.
+ _Javascript(Prototype.js, Scriptaculous), CSS, PHP/MySQL, Google Talk_
+- [metapost package](https://github.com/yogsototh/metautomata) to draw Automata _2003 → 2004_
+ metapost
+- Mac OS X Screensavers ([YClock](https://github.com/yogsototh/YClock) _&_ YAquaBubbles) _2003 → 2004_
+ _Objective-C,Quartz Composer,Cocoa_
+
+You could find even more information by looking at:
+
+- My personnal website: [`http://yannesposito.com`](http://yannesposito.com)
+- My github account: [`http://github.com/yogsototh`](http://github.com/yogsototh)
diff --git a/src/Scratch/files/resume/resume.pdf b/src/Scratch/files/resume/resume.pdf
new file mode 100644
index 0000000..a996aa1
Binary files /dev/null and b/src/Scratch/files/resume/resume.pdf differ
diff --git a/src/Scratch/files/resume/resume.reveal.html b/src/Scratch/files/resume/resume.reveal.html
new file mode 100644
index 0000000..02baa5f
--- /dev/null
+++ b/src/Scratch/files/resume/resume.reveal.html
@@ -0,0 +1,672 @@
+
+
+
+
+Resume
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Clojure Software Engineer for Cisco (Threatgrid), Remote
+
+
+
2013
+
→
+
2016
+
Machine Learning Scientist & Software Engineer at Vigiglobe,
+
+
+
+
+
+
Sophia Antipolis, France
+
+
+
2010
+
→
+
+
Co-Founder of GridPocket, Sophia Antipolis, France
+
+
+
2007
+
→
+
2013
+
AirFrance, Sophia Antipolis, France
+
+
+
10/2006
+
→
+
3/2007
+
Post Ph.D., Hubert Curien Laboratory, St-Etienne, France
+
+
+
10/2004
+
→
+
9/2006
+
ATER (College Degree Teach & Research), Marseille, France
+
+
+
10/2001
+
→
+
9/2004
+
University Monitor (College Degree Teach & Research),
+
+
+
+
+
+
Marseille, France
+
+
+
1995
+
→
+
2000
+
Miscellaneous summer jobs
+
+
+
+
+
+
Education
+
+
+
+
2004
+
CS Ph.D. in Machine Learning at Université de Provence
+
+
+
2001
+
D.E.A. (Equivalent to Master in Computer science)
+
+
+
2000
+
Maîtrise in Computer Science
+
+
+
1999
+
Licence in Computer Science
+
+
+
1998
+
DEUG MIAS (Math)
+
+
+
1995
+
BAC S (Math)
+
+
+
+
+
+
Research Activies: Publications
+
+
+
+
International Journal
+
[Fundamenta Informaticæ, 2008]
+
+
+
+
[Pattern Recognition, 2004]
+
+
+
Internation Conferences
+
[ECML 2008] [ICGI 2006] [COLT 2006]
+
+
+
+
[COLT 2004] [ICALP 2003] [ICGI 2002]
+
+
+
National Journal
+
[JEDAI 2002]
+
+
+
National Conferences
+
[CAp'06] [CAp'04] [CAp'03]
+
+
+
+
+
+
+
+
Presentation
+
I am French with a Post Ph.D in Machine Learning1. Furthermore I love web programming and design.
+
I am currently working remotely for Cisco Security team as a Clojure Software Engineer.
+
Previously I worked for Vigiglobe. The first six months I worked with node.js (API/MongoDB/Web). Then we upgraded our stack to Clojure, Haskell, Mesos, Kafka, Druid, etc... At that time we were two to make all technical decisions. In the end we made a real time analytics of social media content on a scalable architecture. Actually our architecture is able to manage (Aggregation & Machine Learning) thousands of messages per second.2 In particular, I've written an Haskell twitter stream absorber able to handle thousands of tweets per seconds. And I coded myself a real time sentiment analysis module taking algebraic properties into account to optimize its efficiency.
+
+
During my Ph.D. I made a C++ program (github3 and resume4). I coded most of standard HMM learning algorithms. I developed an algorithm which I invented during my Ph.D. which use some operational optimization algorithm. During this period I published articles in international conferences and I taught Computer Science to college students.
+
At the Hubert Curien Laboratory I made my post Ph.D. I developed a scientific application in Java/applet/JWS that should be used by biologists. The code has been updated a bit since my 6 month post Ph.D5.
+
I worked in the web industry for Airfrance. My work environment was quite heterogeneous. From shell scripting to manage huge amount of data, web design and production environment.
+
I worked for GridPocket (I am a co-founder). This is a French startup specialized in Electric Grid. I created a private6 web application.
+
I've also written an iOS application to manage passwords7.
+
I am the author of some quite popular blog posts8.
+
+
For an almost exhaustive list of my projects, you could check my github account: github.com/yogsototh
Vigiglobe architecture able to analyze thousands of social media messages in realtime. In particular, real time Machine Learning & Statistics.
+
YPassword iOS application
+
Gridpocket web services (from conception to realization, works in correlation with a mobile app)
+
DEES: a 10.000 line C++ command line program. This program implement most HMM standard algorithms & inference algorithms.
+
SeDiL: a Java application using Swing UI. The goal is to provide biologist an easy way to use an algorithm that generate Similarity Matrices for strings but also for Tree structures. Most graphics was done by me, including the drawing of trees. I didn't used a library for that purpose.
+
For YPassword ; a Dashboard Widget, a web interface, a command line tool.
Web Application used for private team usage at AirFrance 2008 → This application is just done for teh lulz. Not related to the Airfrance work. But still pleasant. Javascript(Prototype.js, Scriptaculous), CSS, PHP/MySQL, Google Talk
Sécurité informatique: j’ai modélisé un protocole sécurisé (similaire à TOR), une méthode pour gérer de façon sécurisé ses mots de passes, un widget et un script shell pour utiliser cette méthode.
+
+
Mais avant tout j’adore apprendre. Par exemple, j’ai appris de nombreux langages de programmation: Haskell, Clojure, javascript, C, C++, Objective-C, Python, Ruby, Java, Perl, awk, bash, zsh, LaTeX, metapost, camL, Scheme…
Comme Andy Lester me l’a fait remarqué. ack est un simple fichier perl qu’il suffit de copier dans son répertoire personnel ~/bin. Maintenant j’ai ack sur mon serveur professionnel.
Sincèrement, je ne comprend pas qu’ack ne soit pas une commande implémentée par défaut sur les systèmes UNIX. Je ne peux vraiment plus m’en passer, il m’est devenu aussi essentiel qu’un which ou un find.
La plupart du temps c’est suffisant, mais ajouter de la coloration améliore beaucoup l’utilité de cette commande. Il existe déjà un outil pour ça : il s’appelle ack-grep sous Ubuntu. Comme je ne peux pas l’installer sur le serveur de mon entreprise, j’en ai créé un moi-même en quelques lignes :
Lost Highway ne laisse pas indiférent. Le revoir ne lasse pas même s’il parrait complètement obscur. C’est une des forces de David Lynch. Il faut garder à l’esprit qu’il n’existe pas une seule interprétation possible et cohérente du film. Seul David Lynch pourrait donner une explication complète du film. Je donne cependant quelques clés que j’ai découverte qui aident à suivre le film sans être complètement perdu. Ces clés devraient vous aider à vous faire votre propre idée du film…
+
+
La première fois que j’ai vu Lost Highway je me suis senti un peu perdu. J’en ai alors cherché le sens. Voilà ce que j’ai pu trouver sur Internet :
+
+
Fred passe un pacte avec le diable incarné par l’homme en noir ;
+
l’homme mystérieux est une (la) caméra ;
+
seule la première histoire est vrai, la suite étant l’imagination de Fred ;
+
+
sans compter les multiples avis trouvés sur les forums. Tout cela ne me paraissait pas convaincant. J’ai alors réussi à trouver deux articles (en anglais) qui proposent de bien meilleures interprétations. Mais aucun des deux ne m’a complètement convaincu :
le second qui développe presque la même interprétation que la première est vraiment très détaillé sur jasonweb.
+
+
Il faut garder à l’esprit qu’il n’existe pas une seule interprétation possible et cohérente du film. Seul David Lynch pourrait donner l’explication complète du film.
+
Je donne quelques clés aidant à suivre le film sans être complètement perdu. Ces clés devraient vous aider à vous faire votre propre idée du film.
+
Le test de Rorschach
+
+
+
+
À l’instar du protagoniste chacun voit dans ce film ce qu’il a envie d’y voir. Nous pouvons nous y perdre simplement parce que nous pouvons nous perdre dans notre propre esprit. C’est une invitation à la réflexion. Regarder ce film c’est un peu comme passer un test de Rorschach. Qu’y voit-on ? Chacun y met un peu de sa propre personnalité dans l’explication du film.
+
+
Si vous êtes un mystique, vous verrez dans l’homme mystérieux un démon
+
si vous êtes plus psychanalytique vous y verrez une partie inconsciente du protagoniste…
+
+
En général en essayant d’expliquer ce film, on se perd un peu dans notre pensée. Et souvent on échoue à tout expliquer. Il y a toujours un point qui rend la construction incohérente avec le film. C’est pourquoi rechercher une explication unique est un entreprise vaine.
+
Interprétation ≠ Explication
+
Je donne une interprétation et non pas une explication. Ma vision des choses, me semble cohérente. Cependant il est très probable que mon adhésion au film soit très différente de la votre. Il y a certainement beaucoup d’autres explications qui restent cohérentes.
+
J’écris cet article, parce que j’ai l’impression d’en avoir trouver une qui marche pour plus de 97% du film (peut-être 100%, mais j’en doute, il faudrait que je le revois encore une fois).
+
Les clefs du films
+
+
Tout se passe dans la mémoire de Fred
+
+
Tout d’abord, il est clair que comprendre le film comme simplement un film fantastique ne fonctionne pas. En suivant ce point d’entrée on en fini pas de se heurter à des détails incompréhensibles.
+
Mon hypothèse de départ c’est que le film dépeint la représentation de la réalité que s’en fait Fred. Chaque fois qu’il essaye d’échapper à la réalité, celle-ci finira par le rattraper.
+
Fred a commis un acte horrible, un meurtre, et essaye de réparer sa mémoire pour accepter son acte. Il va alors s’inventer des réalitées alternatives.
+
+
Dans un premier temps il tue sa femme (Renée) parce qu’elle le trompe.
+
Dans la deuxième partie, il est plus faible. La version blonde de Renée va le manipuler pour tuer Dick Laurent.
+
Dans la troisième partie il tue Dick Laurent
+
+
Quelle est la validité de ce choix ?
+
Cette interprétation me semble valide à cause du dialogue au début du film avec les policiers qui demandent au protagoniste s’il a une caméra :
+
+
“Do you own a video camera?”
+“No, Fred hates them.”
+“I like to remember things my own way.”
+“What do you mean by that?”
+“How I remember them, not necessarily the way they happened.”
+
+
Ce que l’on peut traduire approximativement par :
+
+
– Avez-vous une caméra ?
+– Non, Fred les détestes.
+– J’aime me rappeler les choses à ma façon.
+– Qu’entendez-vous par là ?
+– Je me rappelle des choses pas nécessairement comme elles se sont passées.
+
+
Ainsi, ce que l’on voit n’est pas la réalité, mais la réalité telle que le conçoit Fred. Il est donc le Dieu de cette réalité. Ainsi les interprétations mystiques faisant intervenir le Diable ont une certaine validité.
+
Qui est l’homme mystérieux ?
+
+
+
+
Qui est donc ce personnage étrange et inquiétant ? Un être capable d’ubiquité qui dit être invité par Fred dans sa maison ? Sans sourcils, le visage blême, les yeux écarquillés fixant sans relâche les faits et gestes de Fred.
+
C’est certainement une des clés du film. À mon avis, il représente la partie mauvaise de Fred. Certainement la jalousie. Si j’étais Catholique je dirai Satan, le tentateur. Il n’agit jamais, mais ne fait qu’observer et filmer. Par contre c’est lui qui donne les armes à Fred pour tuer Dick Laurent. Fred l’a laissé entrer chez lui et il ne peut plus s’en débarrasser. Un peu comme le Iago de Shakespeare est enfermé dans sa jalousie. Le personnage mystérieux prend toute l’importance, il le ronge de l’intérieur. Il aide Fred à accomplir les actes de violences et aussi l’oblige à se souvenir de la réalité.
+
Quand il fait l’amour à Renée il voit le visage de l’homme mystérieux à la place du visage de sa femme. En réalité, il s’agit de la même personne d’après Fred. Ce serait donc elle qui est la source de son mal intérieur.
+
Qui filme et dépose les cassettes ?
+
C’est certainement l’homme mystérieux (ou Fred lui-même) qui est à l’origine de ces cassettes. Le rôle des cassettes est double :
+
+
Rappeler à Fred la réalité. D’après Fred les cassettes video correspondent à la réalité. Il a beau essayer de se cacher la réalité, les cassettes finissent par aller jusqu’au bout et il se voit en train de tuer Renée.
+
La cassette peut aussi faire référence aux cassettes de films pornographique dans laquelle Renée a peut-être tournée dans la réalité ?
+
+
Que s’est-il vraiment passé ?
+
Ici, tout n’est pas donné, on garde une assez grande liberté. Mais on a des indices.
+
Hypothese n°1
+
Je dirais que le protagoniste est un garagiste qui est tombé amoureux d’une actrice porno. Il l’a certainement vu la première fois accompagnant le fameux Dick Laurent. Voyant qu’il ne peut pas l’avoir pour lui, fou de jalousie il tue Dick Laurent dans un motel où celui-ci à couché avec Renée.
+
On a la liberté de décider s’il a vraiment tué la femme ou pas. Dans ma première vision du film, j’avais envie de dire qu’il ne la tue pas. Mais qu’une fois le meurtre commis, il va chez elle, sonne pour lui annoncer la mort de Dick Laurent. Il a alors juste le temps de s’enfuir, la police à ses trousses.
+
Hypothese n°2
+
La première partie resemble à la réalité. Il a vraiment tué sa femme. Il se fait arrété et condamné (certainement à mort). Par contre on ne sait pas s’il est aussi allé tuer Andy.
+
alors c’est laquelle ?
+
La seconde hypothèse me semble plus vraisemblable, car il y a plus de recoupements possibles. La première me semble aussi cohérente. C’est cette première hypothèse que j’avais émise lors de mon premier visionnage du film.
+
Ce qui montre la force de ce film c’est de se dire qu’il y a de nombreuses autres hypothèses qui pourraient aussi bien fonctionner. C’est le fameux effet Rashomon. Plusieurs personnes peuvent décrire de façon cohérentes ce qu’elles ont vu, mais toutes les descriptions sont incohérentes entres-elles.
+
+
Conclusion
+
Il y aurait encore beaucoup à dire sur l’analyse de ce film. Mais il me semble que j’ai rassemblé l’essentiel des clés pour sa compréhension.
+
Il me semble qu’avoir à l’esprit l’effet “test de Rorschach” est essentiel lors de la visualisation de ce film.
+
J’aimerai avoir votre opinion ; mon interprétation tient-elle la route ?
Ma femme a acheté pour environ 500€ (au moins) de séries télé sur iTunes. Mais elles s’est trompé pour la première saison de Battlestar Galactica. Qu’elle a téléchargé en anglais. Hors comme les séries sont protégées, on ne peut simplement pas voir la série avec des sous-titres !
+
+
+WTF?
+
+
+
Résultat des courses, ma femme n’achetera plus de séries sur iTunes tant qu’elles resteront protégées par DRM, qu’elle ne pourront pas être gravées sur DVD (pour être regardées sur la télé). Et comme elle est bien moins prompte à acheter des DVD que simplement cliquer sur un bouton.
+
+
Ca fera nettement moins d’argent pour vous les ayant-droits !!!
+
+
Et ma femme ne pourra pas voir ces épisodes. C’est ce qu’on appelle une cooperation ‘LOSE-LOSE’.
J’utilise Git pour synchroniser des projets personnels. C’est pourquoi quand je crée une branche locale je souhaite quasiment toujours qu’elle soit aussi créée en externe (remote).
+
Voici le script que j’utilise pour accomplir cette tâche :
Màj : Actuellement j’utilise github avec des repository privés. Je paye une somme très raisonnable pour ce service. Si vous voulez être complètement autonome, je vous conseille d’utiliser gitolite sur votre propre serveur accessible sur le web.
+
+
J’utilise Git pour gérer mes projets personnels. J’ai un repository centralisé et tous mes ordinateurs se synchronisent avec lui. Cependant, dans la documentation officielle, je n’ai pas trouvé clairement ce que je souhaitais.
+
En d’autres termes, si vous souhaitez utiliser le type de workflow que SVN proposait avec Git (et ses avantages), voici comment procéder.
+
+
Initialisation
+
Disons que j’ai déjà un projet et que je veuille en créer un nouveau.
Maintenant tous les fichiers du répertoire to/project/directory/ sont versionnés. Si vous voulez ignorer certains fichiers il suffit de modifier le fichier .gitignore.
Màj: La meilleure solution est d’installer gitolite pour installer un serveur git sur sa machine. Gitolite permet de gérer la gestion des droits d’utilisateurs, ceux-ci n’ayant pas accès à un shell sur la machine.
+
+
Maintenant à partir de n’importe quel ordinateur, voici ce que vous pouvez faire :
Je vous conseille de faire la même opération sur l’ordinateur qui à servi à créer le projet de façon à vérifier que tout fonctionne correctement.
+
+
+
L’utilisation courante
+
Pour résumer vous avez maintenant un repository sur Internet et un ou plusieurs ordinateurs lui sont associés. Maintenant il faut que tout soit toujours synchronisé.
+
Avant de commencer à travailler, la première chose à faire est de récupérer les modification à partir d’Internet vers votre poste local :
Bien, maintenant que tout semble bon, il faut encore s’occuper de quelques petites choses (sinon, SVN suffirait). Git est complètement orienté sur la décentralisation et la création de nouvelles branches sur le même poste. Synchroniser des branches sur plusieurs serveurs différent n’est pas une opération naturelle.
+
C’est pourquoi j’ai créé deux simples scripts pour automatiser cette opération. Un script pour créer un branche localement et en ligne. Un autre script pour récupérer les branches en lignes qui ne sont pas présente localement.
+
Ainsi, lorsque je veux créer une nouvelle branche (localement et ligne) ; je lance le script :
+
+git-create-new-branch branch_name
+
+
et quand je suis sur un autre ordinateur et que je veux récupérer les branches crées sur un autre poste, j’exécute :
Comment recompiler un économiseur d’écran sous Snow Leopard(c)
+
Mon économiseur d’écran ne fonctionnait plus sous Mac OS X 10.6 Snow Leopard(c). Après un peu de recherche sous google, le problème semblait pouvoir être réglé avec une recompilation. Cependant, même en recomilant en 64 bits ça ne fonctionnait toujours pas. Après un peu plus de recherches (merci à ElectricSheep ), j’ai découvert les bons paramètres.
+
+
+
+
Pour l’instant je ne l’ai pas compilé pour être compatible Tiger et Leopard. Je ne connais pas assez bien XCode pour savoir comment désactiver le garbage collector sur la version 32 bits et l’activer sur la version 64 bits.
+
Il a été assez difficile de découvrir toutes ces informations. J’espère que cet article aura pu aider quelqu’un.
Que ce soit pour surfer en toute sécurité depuis un accès wifi non sécurisé ou pour contourner les parefeux diaboliques des entreprises. J’ai configuré un serveur ssh écoutant sur le port 443 chez moi.
+
Ensuite de mon portable ou de mon ordinateur local, je dois simplement lancé la merveilleuse commande :
et un proxy socks écoute sur le port 9050. Ce proxy socks transférera toutes les requêtes locales via le tunnel ssh. Ainsi je peux surfer en local comme si je naviguais depuis mon ordinateur à la maison. Je peux écrire mon numéro de carte bleu sans avoir peur que le wifi local soit sniffé. Je dois simplement configurer mon navigateur web pour utiliser le proxy socks sur localhost écoutant le port 9050.
+
J’ai eu cette information à partir de cet article.
+
Ssh et Snow Leopard(c)
+
J’ai un Mac avec Snow Leopard(c) à la maison. Il ne suffit pas de modifier le fichier /etc/sshd.config pour changer le port d’écoute d’sshd. Le système utilise launchd pour lancer les démons.
La plupart du temps je préfère ne pas utiliser le même produit que tout le monde. J’aime bien essayer des choses un peu nouvelles. Mais whosamung.us avait trop de publicités. Je devais affichier une de leur image sur mon site qui n’écrivait que le nombre de personne actuellement présentes. Pas les nombres de visites.
+
C’est pourquoi j’utilise maintenant google analytics. Le problème reste entier pour les navigateurs sans javascript.
iDisk va bientôt disparaître. Cet article est donc presque complètement obsolète.
+
mise à jour du 28/10/2009
+
J’ai mis à jour mon script avec une version incrémentale bien plus pratique. En plus depuis l’écriture de cet article Apple(c) semble avoir nettement amélioré la vitesse de ses serveurs en Europe.
+
+
WebDav terror
+
En France l’iDisk d’Apple(c) est très lent. La vitesse d’upload me rapelle l’époque des modem 56k, c’est dire. La plupart du temps les opérations telles que lister le contenu d’un répertoire prennent au moins 30 secondes (pour 15 éléments). Renommer un répertoire échoue presque systématiquement.
+
Apple(c) utilise des serveurs WebDav pour héberger les fichiers. Le protocole fonctionne sur le port 80 (comme http). Je me suis rendu compte qu’utiliser WebDav via https fontionne bien mieux (2 à 3 fois plus rapide avec moins d’erreurs). Mais, ça reste quand même très lent et insuffisant.
+
J’uploade mes fichiers à partir de mon Mac et de temps en temps à partir d’un PC sous Ubuntu (iDisk monté avec webdavfs).
+
Synchroniser de façon sûre
+
Voici le script que j’utilise pour synchroniser mon site web (non créé avec iWeb(c)) avec le maximum de sécurité. Chaque opération est répétée jusqu’à ce qu’elle fonctionne.
+
Les idées sont :
+
+
Synchroniser vers un répertoire temporaire sur le serveur distant, puis “swapper” les noms des répertoires. Ainsi le site ne reste indisponible que le temps du “swap” du nom des deux répertoires.
+
Réitérer toutes les opérations jusqu’à ce qu’elle aient réussi (par exemple pour le renommage)
+
+
Jusqu’ici j’utilise rsync qui n’est en fait pas plus efficace qu’une simple copie cp avec WebDav. Je devrais utiliser une méthode pour mémoriser les changements entre chaque publication.
+
En réalité quand je suis sur mon Mac j’utilise Transmit qui est vraiment très bien et surtout beaucoup plus efficace que le finder pour synchroniser des fichiers. Ensuite, je ne fait que le “swap” des répertoires.
+
Mon script prend un paramètre -s pour ne faire que le “swap”. Il prend aussi une option -a pour envoyer le fichier index.html qui va rediriger vers ma nouvelle page principale (iWeb(c) à la fâcheuse habitude de le remplacer).
+
Pour utiliser le script vous devriez remplacer la valeur de la variable mobileMeUser par votre nom d’utilisateur mobileMe(c).
In fact this method works for old threads. But it fails to create new post threads. This is why I tried and be conquered by intensedebate, as you can see in the bottom of this page.
+
Remark I didn’t have any comment on my blog when I switched. Therefore my lack of influence was a good thing :-).
+
+
Before begining, I must state that I love Disqus.
+
I know there is a similar blog entry at Trephine.org. Here I just add a straight and easy way to load disqus asynchronously using jQuery.
+
I also know there is a jQuery plugin to make just that. Unfortunately I had some issue with CSS.
+
Now let’s begin.
+
+
Why?
+
Why should I want to load the disqus javascript asynchronously?
+
+
Efficiency: I don’t want my page to wait the complete execution of disqus script to load.
+
More independance: when disqus is down, my page is blocked!
+
+
+
How?
+
I give a solution with jQuery, but I’m certain it will work with many other js library.
If you forget the window.disqus_no_style=true; then your page will be blank. Simply because without this option, the javascript use a document.write action after the document was closed, which cause a complete erasing of it.
+
CSS
+
But with this option you still need to provide a CSS. This is why you have to copy the css code from the embed.js file and rewrite it in a CSS file. You can download the CSS I obtained.
+
+
Now it’s done. I believe all should be fine but I just finished the manip for my own site only 1 hour ago. Therefore there should be some error, tell me if it is the case.
J’ai écrit un article sur la façon dont j’ai essayé d’intégrer Disqus. Mon problème majeur avec Disqus c’était que ma page ne s’affichait pas correctement tant que les commentaire n’avait pas fini de s’afficher. Ça m’est arrivé plusieurs fois d’avoir ma page complètement bloquée parce que les serveurs de Disqus ne répondait pas. C’est pourquoi j’ai essayer de l’inclure de manière asynchrone. Cependant j’ai eu des difficultés pour le faire fonctionner correctement.
+
De plus il n’a pas été trivial de faire en sorte que les commentaires soient commun à plusieurs pages différentes (chaque page à trois représentations différentes, une par language plus une version multi-langue).
+
Je dois reconnaître que je suis un peu triste de quitter Disqus parce que pour chacun de mes problèmes giannii m’a aidé du mieux qu’il a pu. Cependant les problèmes que j’ai eu étaient inhérents à des choix de conceptions plus que de simples petits problèmes techniques.
+
Lorsque j’ai commencé à intégrer Disqus je n’ai jamais essayé Intense Debate. Maintenant que j’ai essayé je doit dire que je suis conquis. Il correspond exactement à ce que j’espérais de ce type de service.
+
Pour le rendre complètement asynchrone il suffit de récupérer leur js commun et de remplacer la ligne suivante :
Here is how I done the tag cloud of my blog. It is done mostly in jQuery. All my site is static and pages are generated with nanoc. It is (in my humble opinion) the modern geek way to make a website.
+
This is why I’ll give only a Ruby Generator, not a full javascript generator. But you can easily translate from Ruby to Javascript.
Calculate the real size of each tag to be displayed.
+
I choosen not to use the full range of size for all the tag. Because if no tag has more than n (here 10) occurences, then it doesn’t deserve to be of the maximal size.
You can download the complete file to put in your ‘lib’ directory. Beware, it is a nanoc 2 version, you’ll have to make some small changes like replace @pages by @items to be nanoc3 compatible.
Voici mon script, il créé tout d’abord un fichier qui contient la liste des fichiers avec leur hash. Afin de les comparer avec ceux qui sont en ligne sans avoir à les parcourir. Ensuite pour chaque fichier qui semble différent, je met à jour le contenu.
+
Cependant même avec ce script j’ai encore des problèmes. Dû à webdav. En particulier le renommage de répertoire. Par exemple :
+
+ mv folder folder2
+
+
Retourne OK et pourtant :
+
+ $ ls folder folder2
+
+
Bouuhh…
+
Pour résoudre ce type de problèmes j’utilise un framework en zsh. Il résout presque tous les problèmes liés à webdav à l’exception du renommage de répertoire.
+
+
#!/usr/bin/env zsh
+
function samelineprint { print -n -P – "\r$*" }
+
avec 1 essai par seconde: 300 = 5 minutes
+
maxessais=300
+
try to create a directory until success
+
function trymkdir { target=“$1” print – mkdir -p $target local essai=1 while ! mkdir -p $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to copy until success
+
function trycp { element=“$1” target=“$2” if [[ ! -d ${target:h} ]]; then trymkdir ${target:h} fi local essai=1 print – cp $element $target while ! $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to remove until success
+
function tryrm { target=“$1” local essai=1 local options=’’ [[ -d $target ]] && options=‘-rf’ print – rm $options $target while ! rm $options $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done essai=1 while [[ -e $element ]]; do samelineprint “rm reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to rename until success
+
function tryrename { element=“$1” target=“$2” local essai=1 while [[ -e $target ]]; do samelineprint “Echec n°$essai le fichier $target existe déjà” ((essai++)) ((essai>maxessais)) && exit 5 sleep 1 done print – mv $element $target while ! mv $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 4 done essai=1 while [[ -e $element ]]; do samelineprint “mv reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
try to move until success
+function trymv { element=“$1” target=“$2” local essai=1 print – mv $element $target while ! mv $element $target; do samelineprint "Echec: essai n°$essai" ((essai++)) ((essai>maxessais)) && exit 5 done essai=1 while [[ -e $element ]]; do samelineprint “mv reussi mais fichier source non disparu n°$essai” sleep 1 ((essai++)) ((essai>maxessais)) && exit 5 done print }
+
+
Et voici le code qui me permet de synchroniser mon site web. Il y a une partie un peu incompréhensible. C’est pour enlever les mail réencodés par le filtre bluecloth qui est une implémentation de markdown. Mes mails, sont encodés à chaque fois de façon différente à chaque réengendrement de page html. C’est pourquoi je les enlève pour ne pas les uploadés inutilement à chaque fois.
+
+
#!/usr/bin/env zsh
+
Script synchronisant le site sur me.com
+
normalement, le site est indisponible le moins de temps possible
+
le temps de deux renommages de répertoire
+
get configuration
+
mostly directories
+
source $0:h/config
+
get trycp function (copy until success)
+
source $0:h/webdav-framework
+
if [[ $1 == ‘-h’ ]]; then print – “usage : $0:h [-h|-s|-d]” print – " -a sychronise aussi l’index" print – " -h affiche l’aide" print – " -d modification directe (pas de swap)" print – " -s swappe simplement les répertoires" fi
+
publication incrementale
+
function incrementalPublish { local ydestRep=destRepsuffix localRef=“$srcRep/map.yrf” print – “Creation du fichier de references” create-reference-file.sh > $localRef remoteRef="/tmp/remoteSiteMapRef.$$.yrf" if [[ ! -e "$ydestRep/map.yrf" ]]; then # pas de fichier de reference sur la cible print – “pas de fichier de reference sur la cible, passage en mode rsync” rsyncPublish swap else trycp “$ydestRep/map.yrf" "$remoteRef” typeset -U filesToUpdate filesToUpdate=( $(diff $localRef $remoteRef | awk ’/1/ {print $2}' ) ) if ((${#filesToUpdate} == 1)); then print – “Seul le fichier ${filesToUpdate} sera téléversé" elif ((${#filesToUpdate}<10)); then print –”${#filesToUpdate} fichiers seront téléversés :" print -- "${filesToUpdate}" else print – “${#filesToUpdate} fichiers seront téléversés” fi # copy all file with some differences # except the map in case of error for element in $filesToUpdate; do if [[ $element == “/map.yrf” ]]; then continue fi if [[ -e srcRepelement ]]; then trycp srcRepelement ydestRepelement else tryrm ydestRepelement fi done # if all went fine, copy the map file trycp $srcRep/map.yrf $ydestRep/map.yrf # remove the temporary file }
+
publication via rsync
+
function rsyncPublish { result=1 essai=1 while (( $result > 0 )); do print – rsync -arv $srcRep/ $destRep.tmp if ((!testmode)); then rsync -arv $srcRep/ destRep.tmpfiresult=? if (( $result > 0 )); then print -P -- "%BEchec du rsync%b (essai n°$essai)" >&2 fi ((essai++)) done }
if [[ “$1” = “-s” ]]; then swap else print -P “Copie de l’init” -f $webroot/Scratch/multi/index.html $webroot/index.html
+
if [[ "$1" = "-d" ]]; then
+ suffix=""
+else
+ suffix=".tmp"
+fi
+print -P -- "%BSync%b[${Root:t} => ${destRep:t}$suffix]"
+incrementalPublish
+fi
+
+
C’est ma façon de remplacer rsync avec des filesystem qui ne permettent pas de l’utiliser. J’espère que ça pourra vous être utile. Je serai heureux de savoir si quelqu’un à une idée sur comment gérer le problème de renommage de répertoire avec webdav.
Pour les développeur de site web Internet Explorer est un cauchemar. C’est pourquoi j’utilise un style complètement différent pour ce navigateur. Avec la librairie jQuery.
+
+$(document).ready( function() { if ($.browser[“msie”]) { // include the ie.js file $(‘head’).append(’
+
+
+
+
+
+
+
+
+
+
Cacher la navigation pour une meilleure concentration
+
+
+
+
+
+
Je crois que le but du minimalisme est de facilité le Focus c’est-à-dire la concentration sur le contenu. Je crois que le minimalisme doit être un moyen et pas une fin. Le Focus devrait être le but, et je pense que le minimalisme n’est pas obligatoire pour l’atteindre.
+
C’est pourquoi mon design n’est pas minimaliste. Mais j’ai décidé d’enlever la majorité des objets servant à la navigation pour améliorer l’attention sur l’article. Peut-être que plus tard, je préfèrerai laisser le menu dans les pages normales du site pour ne le cacher que dans les articles de blog. Pour l’instant je le cache partout.
+
+
Détails techniques
+
Pour ceux qui souhaitent connaître les détails techniques derrière le menu apparaissant/disparaissant, voici le code utilisant jQuery.
J’ai d’abord essayé d’intégrer queryLoader, mais il ne comblait pas mes besoins.
+
Ce plugin ajoutait un ‘div’ noir pour cacher le contenu du site. Cependant, comme le script doit être lancé à la fin du code source. Pendant un petit moment, on peut voir mon site en train de se mettre à jour.
+
Pour cacher ce petit ‘artefact’, voici comment je m’y suis pris.
+
Code
+
D’abort il faut ajouter tout en haut du body cette fois un div qui va être le voile noir qui va tout cacher.
Oui, c’est aussi simple que ça. Maintenant ajouter le #blackpage tout en haut de ma page me permet d’être certain de tout cacher pendant le chargement de la page.
J’ai déjà dit pourquoi je préférais que mon menu de navigation soit caché. J’ai finalement décidé d’attendre un peu avant de cacher le menu. Juste le temps que l’utilisateur le voit. Mais voilà. Comment faire pour qu’il ne disparaisse que lorsque l’on ne s’en sert pas pendant un petit moment ?
Je décris pourquoi j’ai eu tant de mal à me faire à Git. Il y a en effet une partie “non dite” qui m’a bloqué pendant un bon moment. Jusqu’à ce que je découvre le bon document.
+
Le fait est que les branches légères ne sont pas destinée à être des branches isolées. Ainsi, il y a un “workflow standard” qui s’il n’est pas suivi rend l’utilisation de Git inappropriée.
+
+
La décentralisation en action
+
De SVN à Bazaar
+
J’étais un fervent utilisateur de subversion (svn). Lorsqu’un jour, comme beaucoup de gens je découvris ce qu’en pensais Linus Torvald sur une vidéo. Ventant les mérites d’un système de versions concurrentes décentralisé.
+
En effet une fois qu’on s’y intéresse un peu, on voit tous les avantages pratiques qu’apporteraient en théorie un tel système.
+
J’ai alors eu besoin d’un système de version dans mon équipe. Ils n’étaient pas familier avec les systèmes de versions. À par ceux possédant une GUI, qui sont lourds et administrés par un responsable†.
+
Après quelques recherches trois choix se dessinent :
En me renseignant un peu sur les forums et en essayant les trois, je me suis vite rendu compte que celui possédant l’interface utilisateur la plus simple était Bazaar*. Mon choix était fait.
+
De bazaar à Git
+
Je me suis alors familiarisé avec Bazaar. Et je dois dire que c’était vraiment naturel en venant de subversion. La commande pull correspond au update, la commande push correspond au commit. Puis les commandes commit et update existent toujours si on en a besoin et qu’on veut utiliser un workflow identique à celui de subversion.
+
Mais plus le temps passe et plus de partout sur les blog, c’est surtout Git qui a le vent en poupe.
+
Je décide alors d’utiliser Git en particulier pour versionner le site que vous êtes en train de lire. Sauf que je le trouve vraiment difficile d’utilisation et surtout complètement contre intuitif (j’y reviendrai plus tard).
+
Alors que j’essaye de trouver de l’aide et que je dis qu’il est plus difficile à utiliser que Bazaar, beaucoup me répliquent que c’est :
+
+
— Super-tellement-trop-simple que même ma fille de 12 ans qui n’y comprend rien en informatique l’utilise pour versionner ses documents. Elle s’en sert très facilement en créant des branches et tout et tout…
+
+
Bon alors si une gamine 12 ans trouve ça très naturel et que moi (avec mon Doctorat en informatique) j’ai du mal à faire ce que je veux, c’est un peu frustrant et humiliant. Mais qu’est-ce qui fait que Git est naturel aux uns (comme pour CocoaSamurai ) et très confus pour moi ?
+
C’est en lisant un article j’ai enfin compris ce qu’il me manquait. C’est la partie non dite de la conception. Celle que tous les développeurs et les concepteurs trouvaient comme aller de soi. Sauf que pour moi, ce n’était pas du tout le cas.
+
† - Je parle de ClearCase(c). Et oui, je sais qu’il existe des commandes en lignes pour ClearCase(c), mais ce n’est pas comme ça qu’ils étaient habitués à travailler avec des systèmes de “versionning”.
+
* - Je n’ai pas vraiment donné sa chance à Mercurial, la terminologie qu’ils utilisaient était trop éloignée de celle de svn à laquelle je m’étais habituée.
+
+
Lorsqu’on voit les présentations autour de la notion de branche et de dcvs, on s’imagine dans un monde où chaque branche est totalement isolée des autres sauf au moment de “merger” les différences les unes des autres. Tout est magique. C’est la façon de voir “Mondes Parallèles”. Cette façon de voir est expliquée dans le très bon article sur les branches sur betterexplained.
+
Sauf que les concepteurs de Git (conçu pour le noyau Linux) ont plutôt imaginé un système basé non pas autour des mondes parallèles, mais sur la notion de Patch.
+
D’un côté Mondes Parallèles, de l’autre Patchs. Il y a beaucoup de notions équivalentes dans les deux cas, mais aussi quelques différences.
+
+
Bazaar est complètement basé sur la notion de Mondes Parallèles qui va impliquer un phénomène de Patch.
+
Alors que Git est basé sur la notion de Patch qui va impliquer la création de Mondes Parallèles.
+
+
Je ne vais pas argumenté pour savoir si une façon de voir est meilleure que l’autre. Disons simplement que ma façon d’entrer dans l’explication des DCVS était par le biais des Mondes Parallèles alors que Git est conçut selon l’autre notion‡.
+
De la théorie à la pratique
+
Bien que je pense avoir bien compris les mécanismes conceptuels de Git, la mise en pratique posait problème. Et le point noir, celui qui m’empêchait de comprendre Git comme je le souhaitais était dû à la notion de branche légère.
+
Une branche légère qu’est-ce que c’est me demanderez-vous ? Si comme moi on vient de Bazaar, c’est une notion complètement nouvelle. Il s’agit simplement de la capacité de créer une nouvelle branche en réutilisant le répertoire dans lequel on se trouve.
+
En pratique pour changer de branche, il faut lancer une commande. Tous les fichiers locaux non modifiés depuis le dernier commit seront alors modifiés pour correspondre à la version de la branche.
+
En théorie, les branches légères sont des branches tout comme avec bazaar. D’ailleurs le mot utilisé n’est pas branche légère mais branche tout court.
+
Sauf que contrairement à une branche standard résidant dans son propre répertoire, une branche légère est destinée à n’être qu’un patch de la branche principale du répertoire dans lequel elle réside.
+
Bien entendu on pourra m’objecter que l’on peut tout à fait utiliser ces branches légères comme des branches normales. Mais elles n’ont pas été conçues pour ça. Et donc, en pratique, c’est gênant de les utiliser de la sorte.
+
Voici comment Git est censé être utilisé (pour plus de détails vous pouvez lire Git for Designers en anglais) :
+
+
récupération ou création d’un “repository” central Le Grand Repository
+
Création d’un branche légère locale qui contient les différences qui vont devoir être “patché” dans LE GRAND REPOSITORY.
+
+
Voici comment n’est pas censé être utilisé Git :
+
+
Récupération ou création d’un “repository” quelconque
+
Création d’un branche légère locale qui n’a pas pour vocation de mettre à jour le “repository” d’origine, mais de vivre sa vie de façon autonome et de récupérer les mises à jour des autres du “repository” d’origine.
+
+
En effet cette petite notion m’a empêché de fonctionner correctement.
+
En pratique
+
Maintenant que j’ai compris ça, je peux enfin comprendre pourquoi Git a tant de défenseurs qui continue de trouver que Git est meilleur que les autres.
+
La notion de branche légère est essentielle à Git et vraiment utile en pratique. Notamment pour la gestion de ce site. Par contre, elle m’empêche d’utiliser les branches comme je le souhaiterai.
+
Mais dans ce cas-là, je n’ai qu’à utiliser des clônes et pas des branches légères.
+
Des exemples
+
Je trouve toujours que les terminologies de bazaar sont plus claires et plus concises.
+
+bzr revert
+
+
est quand même plus clair que
+
+git reset –hard HEAD
+
+
De la même façon
+
+bzr revert -r -3
+
+
je trouve ça mieux que
+
+git reset –hard HEAD~3
+
+
Là ça va commencer à se compliquer. Si on veut revenir dans le temps sur toute l’arborescence, avec Git on utilise reset.
+
+OK
+
+
Maintenant si je veux revenir dans le temps sur un seul fichier. Naturellement on se dit :
+
+git reset –hard FILE
+
+
+ET BIEN NON !
+
+
La solution c’est :
+
+git checkout FILE
+
+
Quoi ? checkout !? Bon, d’accord, j’accepte, pourquoi pas après tout ? En plus quand on est habitué à Bazaar c’est :
+
+git revert FILE
+
+
Ce que je trouve quand même bien plus naturel.
+
Mais là où ça devient vraiment difficile de s’y faire c’est pour changer de branche.
+Avec Bazaar ça donne :
+
+cd ../branch
+
+
Bon ok, il faut changer de répertoire, un répertoire par branche. Ça consomme de l’espace disque mais au moins on voit où on est. Avec Git voilà comment on change de branche (branche légère) :
+
+git checkout branch
+
+
Alors là, on se dit “WTF?” ; en français : mais qu’est-ce que c’est que ça ? Je croyais que checkout c’était pour récupérer l’état d’un fichier ?
+
En fait le mot checkout sert à la fois à revenir en arrière sur un fichier (MAIS PAS TOUTE UNE ARBORESCENCE où là ça sera reset --hard) et à changer de branche !
+
Je trouve ça carrément contre nature. Même si c’est totalement justifié du point de vue théorique voir le très bon article (en anglais) Git for Computer Scientist. Du point de vue interface utilisateur, on peut difficilement faire pire. On dirait que les mots clés sont utilisés pour piéger l’utilisateur.
+
+
+
— Alors, essaye de deviner ce qu’il va falloir écrire pour faire cette opération ?
+
— Perdu. Essaye encore, cherche sur Internet (blaireau).
+
— Non, c’est toujours pas bon, recommence (sale nul).
+
+
+
Bon alors, voilà, les défauts de Git. Mais, il a par contre beaucoup d’avantages. Une fois qu’on a compris le principe des branches légères. Tout devient plus clair. Même si en pratique, l’édition du fichier .git/config peut s’avérer un peu fastidieuse et surtout contre intuitive.
+
‡ - Il faut aussi préciser qu’ayant travaillé sur les logiques multi-modales et en particulier sur les logiques temporelles (linéaires ou non), j’étais plus enclin à adhérer à cette vision des choses. “Ah mes premiers amours dans la recherche scientifique !”
+
+
Conclusion
+
DCVS vs. CVS ?
+
Est-ce que ça valait la peine d’utiliser un système de “versionning” décentralisé ? Indéniablement la réponse est oui. On peut très bien vivre avec des systèmes de versions centralisés, mais la souplesse apportée par la facilité de “merger” différentes branches. De travailler de façon autonomes sur différentes parties d’un projets sont vraiment des plus appréciables. J’aurai vraiment du mal à revenir en arrière.
+
Est-ce que Git est meilleurs que Bazaar ?
+
En terme de fonctionnalités je dirai que Git est meilleurs. Par contre, je dois avouer qu’il s’agit d’un CVS qui s’est mis dans mes pattes. Or c’est exactement ce que je ne souhaitait pas lors de mon premier choix.
+
Je n’aurai pas dû avoir du mal à comprendre cette notion de branche légère qui doit être un patch sinon tu reçois des messages t’expliquant que tu es en retard. En réalité, Git différencie la notion d’arbre de la notion de branche. Ce qui n’est pas le cas dans Bazaar. Conceptuellement, c’est beaucoup plus simple de comprendre avec Bazaar.
+
Finalement ?
+
Pour conclure, j’utilise plus souvent Git que Bazaar et je dois dire que je préfère utiliser Git. Cependant, les commandes comme revert manquent cruellement avec Git. Pour l’instant je n’ai pas encore fait d’alias pour renommer les commandes Git comme je le souhaite.
Voici un tutoriel Git détaillé pour ceux qui en connaissent très peu sur les systèmes de versions. Vous comprendrez l’utilité de tels systèmes et surtout comment on se sert des systèmes de versions modernes, le tout en restant le plus pragmatique possible.
+
+
+
Pour commencer, la conclusion
+
Voici la liste des commandes nécessaires et suffisantes pour utiliser Git. Il y en a très peu. Il est normal de ne pas les comprendre tout de suite mais c’est pour vous donner une idée. Malgré la longueur de l’article, 95% de l’utilisation de Git tiens dans les 7 commandes décrites ci-après.
Cet article est écrit pour ceux qui en savent très peu sur les systèmes de version. Il est aussi écrit pour ceux qui n’ont pas suivi les progrès accomplis depuis CVS ou subversion (SVN). C’est pourquoi, dans un premier temps, j’explique rapidement quels sont les buts poursuivis par les systèmes de versions. J’explique ensuite comment installer et configurer Git. Puis, pour chaque action que doivent accomplir les DCVS je donne les commandes Git qui y correspondent.
Si tout ce qui vous intéresse c’est d’utiliser Gittout de suite. Lisez simplement les parties sur fond noir. Je vous conseille aussi de revenir relire tout ça un peu plus tard, pour mieux comprendre les fondements des systèmes de versions et ne pas faire de bêtises quand vous les utilisez.
+
+
Git est un DCVS, c’est-à-dire un système de versions concurrentes décentralisé. Analysons chaque partie de cette appellation compliquée.
+
Système de versions
+
Tout d’abord, les systèmes de versions gèrent des fichiers. Quand on travaille avec des fichiers sans système de version voilà ce qui arrive souvent :
+
Lorsqu’on modifie un fichier un peu critique et qu’on a pas envie de perdre, on se retrouve souvent à le recopier sous un autre nom. Par exemple
Du coups, ce nouveau fichier joue le rôle de backup. Si on casse tout, on peut toujours écraser les modifications que nous avons faites. Évidemment le problème avec cette façon de faire c’est que ce n’est pas très professionnel. Et puis c’est un peu limité. Si on veut faire trois ou quatre modifications on se retrouve avec plein de fichiers. Parfois avec des nom bizarres comme :
Bon alors si on veut que ça marche il faut se fixer des conventions de nommage. Les fichiers prennent beaucoup de place alors que souvent il n’y a que quelques lignes différentes entre le fichier et son backup…
+
Heureusement les systèmes de version viennent à la rescousse.
+
Il suffit de signaler que l’on va faire une nouvelle version d’un fichier et le système de version se débrouille pour l’enregistrer quelque part où on pourra facilement le retrouver. Et en général, le système de version fait les choses bien. C’est-à-dire qu’il n’utilise que très peu d’espace disque pour faire ces backups.
+
Il fut un temps où les versions étaient gérées fichier par fichier. Je pense à CVS. Puis on s’est vite aperçu qu’un projet c’est un ensemble de fichiers cohérents. Et donc il ne suffit pas de pouvoir revenir en arrière par fichier, mais plutôt dans le temps. Les numéros de versions sont donc passé d’un numéro par fichier à un numéro par projet tout entier.
+
Ainsi on peut dire, «je veux revenir trois jours en arrière», et tous les fichiers se remettent à jour.
+
+
Qu’apportent les systèmes de versions ? (je n’ai pas tout mentionné)
+
+
backup automatique de tous les fichiers: Revenir dans le temps. ;
+
donne la possibilité de voir les différences entre chaque version et les différences entre la version en cours et les modifications locales ;
+
permet de poser un tag sur certaines versions et ainsi pouvoir s’y référer facilement ;
+
permet d’avoir un historique des modifications. Car en général il est demandé aux utilisateurs d’ajouter un petit commentaire à chaque nouvelle version.
+
+
+
concurrentes
+
Les systèmes de versions sont déjà intéressants pour gérer ses projets personnels. Car ils permettent de mieux organiser celui-ci. De ne (presque) plus se poser de questions à propos des backups. Je dis presque parce qu’il faut quand même penser à protéger par backup son repository. Mais là où les systèmes de versions deviennent vraiment intéressants, c’est pour la gestion de projets à plusieurs.
+
Commençons par un exemple avec un projet fait par deux personnes ; Alex et Béatrice. Sur un fichier contenant une liste de dieux Lovecraftiens :
Disons que Alex est chez lui, il modifie le fichier :
+
+
+Cthulhu
+Shubniggurath
+Soggoth
+Yogsototh
+
+
+
puis il envoi ce fichier sur le serveur du projet. Ainsi sur le serveur, il y a le fichier d’Alex.
+
Ensuite c’est Béatrice qui n’a pas récupéré le fichier d’Alex sur le serveur qui fait une modification.
+
+
+Cthulhu
+Dagon
+Shubniggurath
+Yogsototh
+
+
+
Puis Béatrice envoi son fichier sur le serveur.
+
La modification d’Alex est perdue. Encore une fois les systèmes de versions sont là pour résoudre ce type de soucis.
+
Un système de version aurait mergé les deux fichiers au moment où Béatrice voulait envoyer la modification sur le serveur. Et comme par magie, sur le serveur le fichier deviendra :
En pratique, au moment où Béatrice veut envoyer ses modifications, le système de version la préviens qu’une modification a eu lieu sur le serveur. Elle utilise la commande qui rapatrie les modifications localement et qui va mettre à jour le fichier. Ensuite Béatrice renvoie le nouveau fichier sur le serveur.
+
+
Qu’apportent les Systèmes de Versions Concurrentes ?
+
+
récupérer sans problème les modifications des autres ;
+
envoyer sans problème ses modifications aux autres ;
+
permet de gérer les conflits. Je n’en ai pas parlé, mais quand un conflit arrive (ça peut arriver si deux personnes modifient la même ligne avec deux contenus différents), les SVC proposent leur aide pour les résoudre. J’en dirai un mot plus loin.
+
permet de savoir qui a fait quoi et quand
+
+
+
décentralisé
+
Ce mot n’est devenu populaire que très récemment dans le milieu des systèmes de version. Et bien ça veut dire principalement deux choses.
+
Tout d’abord, jusqu’à très récemment (SVN) il fallait être connecté sur un serveur distant pour avoir des informations sur un projet. Comme avoir l’historique. Les nouveaux systèmes décentralisés permettent de travailler avec un REPOSITORY (le répertoire contenant tous les backups, et les différentes info nécessaires au fonctionnement du système de versions) local au projet. Ainsi on peut avoir l’historique du projet sans avoir à se connecter au serveur.
+
Toutes les instances de projets peuvent vivre de façon indépendantes.
+
Pour préciser, les systèmes de versions concurrentes décentralisés sont basés sur la notion de branche.
+
Et la signification pratique est très importante. Ça veut dire que tout les utilisateurs travaillent de façon complètement indépendante les uns des autres. Et c’est l’outil de version qui se charge de mettre tout ça ensemble.
+
Ça va même encore plus loin. Ça permet de développer plusieurs features de manière complètement indépendantes. Sous les autres systèmes c’était plus difficile.
+
L’exemple type :
+
+
Je développe mon projet. Je suis en train de l’améliorer. Lorsqu’un bug urgent est reporté.
+
Je peux très facilement avec un système décentralisé, revenir sur la version qui pose problème. Résoudre le bug. Renvoyer les modifications. Puis revenir à ma version avec les améliorations en cours. Et même récupérer la correction de bug dans ma nouvelle version avec les améliorations.
+
Dans un système non décentralisé, cela est possible, mais fastidieux. Les systèmes décentralisés rendent ce type de comportement très naturels. Ainsi, il devient naturel de tirer des branches pour toutes les features, les bug…
+
+
+
Avantages donnés par la décentralisation des systèmes de versions concurrentes :
+
+
Possibilité de travailler sans être connecté au serveur de version ;
+
Possibilité de créer beaucoup de patches atomiques ;
+
Grande facilité de maintenance de plusieurs versions différentes de la même application.
+
+
+
Pour résumer
+
Résumons l’ensemble des choses que l’on peut faire facilement avec un DCVS :
+
Systèmes de versions
+
+
revenir dans le temps ;
+
lister les différences entre chaque version ;
+
nommer certaines versions pour s’y référer facilement ;
+
afficher l’historique des modifications.
+
+
Concurrentes
+
+
récupérer les modifications des autres ;
+
envoyer ses modifications aux autres ;
+
permet de savoir qui a fait quoi et quand ;
+
gestion des conflits.
+
+
Décentralisé
+
+
manipuler facilement des branches
+
+
Maintenant voyons comment obtenir toutes ces choses facilement avec Git.
Enregistrez le fichier suivant comme le fichier ~/.gitconfig.
+
[color]
+ branch = auto
+ diff = auto
+ status = auto
+[alias]
+ st = status
+ co = checkout
+ br = branch
+ lg = log --pretty=oneline --graph
+ logfull = log --pretty=fuller --graph --stat -p
+ unstage = reset HEAD
+ # there should be an article on what this command do
+ uncommit = !zsh -c '"if (($0)); then nb=$(( $0 - 1 )); else nb=0; fi; i=0; while ((i<=nb)); do git revert -n --no-edit HEAD~$i; ((i++)); done; git commit -m \"revert to $0 version(s) back\""'
+ undomerge = reset --hard ORIG_HEAD
+ conflict = !gitk --left-right HEAD...MERGE_HEAD
+ # under Mac OS X, you should use gitx instead
+ # conflict = !gitx --left-right HEAD...MERGE_HEAD
+[branch]
+ autosetupmerge = true
+
Vous pouvez obtenir le même résultat en utilisant pour chaque entrée la commande git config --global. Configurez ensuite votre nom et votre email. Par exemple si vous vous appelez John Doe et que votre email est john.doe@email.com. Lancez les commandes suivantes :
Voilà, la configuration de base est terminée. J’ai créé dans le fichier de configuration global des alias qui vont permettre de taper des commandes un peu plus courtes.
+
Récupération d’un projet déjà versionné
+
Si un projet est déjà versionné avec Git vous devez avoir une URL pointant vers les sources du projet. La commande a exécuter est alors très simple.
S’il n’y a pas de serveur git sur le serveur distant, mais que vous avez un accès ssh, il suffit de remplacer le git de l’url par ssh. Pour ne pas avoir à entrer votre mot de passe à chaque fois le plus simple est de procéder comme suit :
Répondez aux question et n’entrez surtout PAS de mot de passe. Ensuite copiez les clés sur le serveur distant. Ce n’est pas la façon la plus sûre de procéder. L’idéal étant d’écrire quand même un mot de passe et d’utiliser ssh-agent.
+
Ensuite le plus simple, si vous possédez ssh-copy-id (sous Ubuntu par exemple) :
Une petite précision. Si vous ne souhaitez pas versionner tous les fichiers. Par exemple, les fichiers de compilations intermédiaires. Alors il faut les exclure. Pour cela, avant de lancer la commande git add .. Il faut créer un fichier .gitignore qui va contenir les pattern que git doit ignorer. Par exemple :
Maintenant si vous voulez créer un repository sur un serveur distant, il faut absolument qu’il soit en mode bare. C’est-à-dire que le repository ne contiendra que la partie contenant les informations utile à la gestion de git, mais pas les fichiers du projet. Sans rentrer dans les détails, il suffit de lancer :
Vous avez maintenant un répertoire sur votre ordinateur local. Il est versionné. Vous pouvez vous en rendre compte parcequ’à la racine (et à la racine seulement), il y a un répertoire .git. Ce répertoire contient tous les fichiers nécessaires au bon fonctionnement de Git.
+
Il ne reste plus qu’à savoir comment s’en servir maintenant pour obtenir toutes les jolies promesses faites dans la première partie.
+
Et c’est parti !
+
Voici une parmi de nombreuses autres façon d’utiliser Git. Cette méthode est nécessaire et suffisante pour travailler seul ou en collaboration sur un projet commun. Cependant, on peut faire beaucoup mieux avec Git que ce workflow (en langage anglo-saxon).
verifier le details de ses modifications git status et git diff
+
indiquer si nécessaire que de nouveaux fichiers doivent être versionnésgit add [file]
+
enregistrer ses modifications git commit -a -m "message"
+
envoyer ses modifications aux autres git push (refaire un git pull si le push renvoie une erreur).
+
+
+
Voilà, avec ces quelques commandes vous pouvez utiliser Git sur un projet avec d’autres personnes. Même si c’est suffisant, il faut quand même connaître une chose avant de se lancer ; la gestion des conflits.
+
Gestion des conflits
+
Les conflits peuvent survenir lorsque vous modifiez les même lignes de codes sur le même fichier d’une autre branche que vous mergez. Ça peut sembler un peu intimidant, mais avec Git ce genre de chose est très facile a régler.
Maintenant vous êtes fin prêt pour utiliser Git. Sauf que Git, c’est un outil qui permet de faire beaucoup plus que juste ça. Nous allons maintenant voir comment utiliser les fonctionnalités de Git qui n’étaient pas disponibles avec CVS et consorts.
+
Pourquoi Git est cool ?
+
Parce que grace à Git vous pouvez travailler sur plusieurs partie du projet de façon complètement isolée les unes des autres. Ça c’est la partie décentralisée de Git.
+
Toutes les branches locales utilisent le même répertoire. Ainsi on peu changer de branche très aisément et rapidement. On peut aussi changer de branche alors que certains fichier sont en cours de modifications. On peut même pousser le vice jusqu’à modifier un fichier, changer de branche, commiter une partie seulement des modifications de ce fichier dans la branche courante. Revenir dans l’ancienne branche et commiter à nouveau les modifications restantes. Et merger dans une troisième branche les deux modifications.
+
Avec la command git rebase on peut après coup, décider que certaines modifications devaient aller dans certaines branches, que d’autres ne servaient à rien. C’est une commande vraiment très puissante pour organiser l’historique.
+
En pratique, qu’est-ce que ça signifie ? Mieux qu’avec tous les autres systèmes de versions, vous pouvez utiliser Git pour vous concentrer sur votre code. En effet, on peut envoyer les commits après avoir coder. Par exemple, vous pouvez coder sur la résolution du bug b01, du bug b02 et de la feature f03. Puis ensuite, vous pouvez créer une branche par bug et par feature. Puis commiter les modifications pour chaque branche et chaque feature. Puis finalement merger tous les modifications dans la branche principale.
+
Tout a été pensé pour vous permettre de coder d’abord, puis de vous occuper du système de version plus tard. Bien entendu, faire des commit atomique au fur et à mesure du code permet de gagner du temps et de ne pas trop s’embêter pour organiser les branches. Mais rien ne vous y oblige. Par contre faire la même chose dans d’autres systèmes de versions n’est absolument pas naturel.
+
Avec Git vous pouvez aussi dépendre de plusieurs sources. Ainsi, plutôt que d’avoir un serveur centralisé, vous pouvez avoir plusieurs sources. Vous pouvez définir ce genre de chose très finement.
+
Ce qui change le plus avec Git c’est la vision d’un projet centralisé sur un serveur avec plusieurs personnes qui travaillent dessus. Avec Git plusieurs personnes peuvent travailler sur le même projet, mais sans nécessairement avoir un repository de référence. On peut très facilement résoudre un bug et envoyer le patch à plein d’autres versions du projet.
+
Liste de commandes
+
Les commandes pour chaque choses
+
Dans la première partie, nous avons vu la liste des problèmes résolus par Git. En résumé Git doit pouvoir :
+
+
récupérer les modifications des autres ;
+
envoyer ses modifications aux autres ;
+
revenir dans le temps ;
+
lister les différences entre chaque version ;
+
nommer certaines versions pour s’y référer facilement ;
Il est vraiment incroyable que le filtrage d’appel soit impossible avec un iPhone ! Le seul intérêt que j’y vois, c’est une négociation avec les opérateurs pour interdire aux utilisateurs de passer à travers la publicité. C’est tout simplement inacceptable.
+
Je suis un utilisateur λ de l’iPhone. Le seul moyen de filtrer ses appels, de faire des blacklists ou autre c’est de jailbreaker son iPhone. Et je n’en ai aucune envie. Alors si comme moi, vous trouvez ça inacceptable, envoyez un mot à Apple : http://www.apple.com/feedback/iphone.html
Même si je considère que git a beaucoup de points noirs, je pense qu’il reste le meilleur DCVS à ce jour avec lequel travailler. C’est pourquoi je commencerai par parler des qualité de bazaar qui manquent à git. Ensuite seulement je donnerai le seul avantage de git qui suffit à le rendre préférable à Bazaar.
+
+
La découverte des DCVS
+
À savoir avant de débuter l’article. Je suis, comme beaucoup, un ancien utilisateur de subversion. Je trouve subversion très bien, mais j’ai été conquis par les capacités supplémentaires apportées par les systèmes de versions concurrentes décentralisés.
+
Il y a deux façon de percevoir les systèmes de versions. Soit on voit un système de branches (voir le très bon article sur betterexplained). Soit on peut voir les systèmes de versions comme des moyens d’appliquer des patches. C’est-à-dire que l’on peut soit se concentrer sur les nœuds soit sur les transitions du graphe induit par les différentes versions d’un projet.
+
Pour git, c’est plutôt ce deuxième point de vue qui a été adopté. C’est un peu normal, étant donné que c’est Linus Torvald qui l’a inventé pour combler les problèmes inhérent aux problèmes de création de code dans le noyau Linux. Et comme historiquement, la communauté Linux se base beaucoup sur les patches, il semblait logique que ce soit ce second point de vue qui soit adopté.
+
J’ai d’abord été convaincu par Bazaar. Pourquoi ? Les arguments en faveur de bazaar étaient : facilité d’utilisation en particulier, facilité d’adaptation pour tous ceux qui étaient habitués à subversion. Comme c’était mon cas, et que lorsque j’avais essayé de suivre la doc Git à l’époque c’était un peu épique. Puis avec le temps, je me suis dit que je n’allais quand même pas mourir idiot et que j’allais me mettre sérieusement à git histoire de voir si ses défenseurs avaient vraiment raison.
+
Mon dieu, que ce fut fastidieux. La terminologie est affreuse ! Et ce n’est rien de le dire.
+
Là où Bazaar est meilleur que git
+
Par exemple, checkout qui sert certainement à la même chose du point de vue technique, est dans l’usage un terme employé pour faire des actions qui semblent très différentes à un utilisateur λ. Exemple :
+
+ git checkout pipo
+
+
annule une modification courante du fichier pipo
+
+ git checkout pipo
+
+
change de la branche courante vers la branche pipo
+
Et là, comme moi, vous remarquez que la même commande à deux sens complètement différents. Comment ça se passe alors, quand il y a une branche pipo et un fichier pipo alors ? Et bien par défaut, ça change de branche. Pour lever l’ambigüité il faut utiliser la syntaxe
+
+ git checkout ./pipo
+
+
Oui, bon… Voilà, voilà, voilà….
+
Ça marche, mais ce n’est pas très convivial. D’autant plus que le mot clé checkout signifiait sous CVS et SVN l’opération pour récupérer un projet distant.
+
Là où la différence se creuse c’est avec la terminologie Bazaar qui est bien plus naturelle. Car il n’y a pas de commande pour changer de branche, puisqu’il y a une branche par répertoire. Ainsi, pour changer de branche, il suffit de faire cd path/to/branch. Et pour revenir en arrière :
+
+ bzr revert pipo
+
+
De plus, la plupart des commandes bazaar prennent en paramètre un numéro de révision, par exemple pour revenir 3 versions précédentes il suffit d’écrire :
+
+ bzr revert -r -3 pipo
+
+
L’équivalent sous git est beaucoup plus cryptique :
+
+ bzr checkout HEAD~3 pipo
+
+
Encore un fois, Bazaar est bien plus lisible.
+
Revenir dans le temps pour tout le projet :
+
avec Bazaar :
+
+ bzr revert -r -3 pipo
+
+
et avec git ? git checkout ? Bien sûr que non voyons ! Ce serait bien trop simple. Ce que l’on trouve dans les forums c’est :
+
+ git reset –hard HEAD~3
+
+
Sauf que cette syntaxe est horrible. Elle oublie ‘réellement’ les révisions. Il faut donc l’utiliser avec prudence. Mais en effet, je conseillerai plutôt :
Histoire d’avoir la branche backup sous la main, car sinon, on risque de perdre définitivement la version courante de HEAD. Qui ramène la branche locale à ce point. Mais il reste des erreur s’il y a eu des ajouts de fichier entre temps. Le seul et l’unique vraiment propre de revenir en arrière dans git c’est de lancer la commande suivante :
+
+ for i in (seq02); dogitrevert − n − − no − editheadi; done git commit -m “reverted 3 versions back”
+
+
ce qui signifie sur un système UNIX en zsh (ou bash) faire git revert de toutes les dernières versions. Même si quelqu’un d’autre à fait un pull de vos modification intermédiaire il ne sera pas embêté et il sera au courant de ce qu’il s’est passé.
+
La règle est simple : Ne JAMAIS utiliser la commande git reset avec une version que d’autres personnes auraient pu avoir fetcher.
+
Voilà, c’est dit. Découvrir ça m’a pris pas mal de temps, avec plein d’essai de tous les cotés. Le plus sûr reste toujours la méthode vue plus haut. Si vous souhaitez automatiser cela, le plus simple est d’ajouter l’alias suivant à votre fichier ~/.gitconfig. Bien sûr l’alias ne fonctionnera que sur les environnement possédant zsh, ce qui est le cas de la plupart des environnements UNIX (Ubuntu, Mac OS X…).
Ce qui fait que git est le meilleur DCVS jusqu’à aujourd’hui
+
Après avoir énoncé les cotés négatifs (et je les trouve nombreux) de git. Voici les cotés positifs qui a eux seul valent la peine de se coltiner tous les problèmes inhérent à git.
+
Cheap branching
+
Vous travaillez toujours dans le même répertoire principal. Par exemple, vous pouvez travailler sur deux corrections de bug. Disons fix1 et fix2 nécessitant la modification respective de file1 et file2. Vous pouvez travailler dans n’importe quel ordre sur vos deux fichiers dans la branche master. Puis, une fois votre travail fini. Aller dans la branche fix1 pour commiter file1. Puis aller dans la branche fix2 pour commiter file2. Et enfin, merger les deux branches dans master.
Et il est vraiment très agréable de ne pas se soucier d’être dans la bonne branche. Vous n’avez à vous occuper que de votre code et seulement ensuite vous occuper du système de version.
+
Sous Bazaar, il m’est souvent arriver de coder dans la mauvaise branche. Pour récupérer le coup, on doit copier les modifications du fichier dans la bonne branche et faire un revert sur le fichier en question, puis aller dans la bonne branche pour commiter les modifications. Enfin, la plupart du temps, je me trompe de branche et puis tant pis, je merge les deux tout en sachant que c’est sale.
+
C’est pourquoi je préfère utiliser git. Si Bazaar venait à implémenter ce système de cheap branching, je le replacerai certainement en tête.
Je viens de trouver le moyen de changer son shell par défaut sous Mac OS X. Cette note est plus pour moi. Mais elle peut aussi servir à quelqu’un d’autre. Il suffit de lancer la commande :
+ if str.match(regexp) and not str.match(other_regexp) do_something
+
+
et vous devez obtenir le même comportement avec seulement une expression régulière. Le problème c’est que le complémentaire des régulier n’est pas régulier. Celà peut s’avérer impossible.
+
Cependant, pour certaines expressions ce peut être possible†. Disons que vous souhaitez matcher toutes les lignes contenant le mot bull, mais que vous ne souhaitez pas matcher bullshit. Voici une façon sympa d’y arriver :
+
+
# matcher toute les chaines qui # matchent ‘bull’ (bullshit compris) /bull/
Regardons de plus près. Dans la première ligne, l’expression est : bull([^s]|$), pourquoi avons nous besoin du $ ? Parce que sans lui, le mot bull ne serait pas matché. Cette expression signifie :
+
+
La chaine finie par bull
+ou,
+contient bull suivi d’une lettre différente de s.
+
+
Et voilà. J’espère que ça a pu vous aider.
+Notez que cette méthode n’est pas toujours la meilleure. Par exemple essayons d’écrire une expression régulière équivalente à l’expression conditionnelle suivante :
+
+ # Commence avec ‘a’: ^a # Se finit par ‘a’: c$ # Contient ‘b’: .b. # Mais n’est pas ‘axbxc’ if str.match(/^a.b.c/)andnotstr.match(/axbxc/) do_something end
+
Cette solution utilise la longueur maximale de la chaine qui ne doit pas être matchée. Il existe certainement d’autres méthodes. Mais la leçon importante c’est qu’il n’est pas naturel d’exclure des solutions d’un expression régulière.
+
+
† Il peut être démontré que tout ensemble régulier privé d’un ensemble fini est aussi régulier.
Dans mon précédent article j’ai donné certaines astuces pour matcher ‘tout sauf quelque chose’. De la même manière, un truc pour matcher la chaine de caractère la plus petite possible. Disons que vous voulez matcher la chaine de caractère entre ‘a’ et ‘b’. Par exemple, vous voulez matcher :
+
+a.....a......b..b..a....a....b...
+
+
Voici les deux erreurs communes et une solution :
+
+/a.*b/
+a.....a......b..b..a....a....b...
+
+
La première erreur vient de l’utilisation du terrible.*. Parce que vous allez matcher la chaîne de caractère la plus longue possible.
+
+/a.*?b/
+a.....a......b..b..a....a....b...
+
+
L’autre manière naturelle de répondre à ce problème est de changer la greediness. Mais ce n’est pas assez parce que vous allez matcher du premier a au premier b après celui-ci. On peut alors constater que votre chaine de caractère ne devrait comprendre ni la lettre a ni la lettre b. Ce qui emène à la dernière solution élégante.
+
+/a[^ab]*b/
+a.....a......b..b..a....a....b...
+
+
Jusqu’ici, c’était facile. Maintenant comment fait vous quand au lieu de a vous avez une chaine de caractère ?
Oui, c’est un peu compliqué. Mais que se passe t’il lorsque la chaine de caractère que vous voulez matcher est encore plus longue que <li> ?
+
Voici un algorithme qui permet de résoudre ce problème aisément. Vous devez réduire ce problème au premier. C’est-à-dire celui avec une seule lettre :
+
# transforme un simple caractère choisi aléatoirement
+# en un identifiant unique
+# (vous devez vérifier que l'identifier est VRAIMENT unique)
+# attention l'identifiant unique ne doit pas
+# contenir le caractère choisi.
+s/X/_was_x_/g
+s/Y/_was_y_/g
+
+# transforme la longue chaine de caractère
+# en un seul caractère
+s/<li>/X/g
+s/<\/li>/Y/g
+
+# Utilisation de la première méthode
+s/X([^X]*)Y//g
+
+# Retransformation des lettres en chaines
+# de caractères
+s/X/<li>/g
+s/Y/<\/li>/g
+
+# retour des anciens caractères.
+s/_was_x_/X/g
+s/_was_y_/Y/g
+
Et ça fonctionne en seulement 9 lignes pour toute chaine de début et de fin. Cette solution fait un peu moins I AM THE GREAT REGEXP M45T3R, URAN00B, mais elle est mieux adaptée à mon avis. De plus, utiliser cette dernière solution prouve que vous maitrisez les expressions régulières. Simplement parce que vous savez qu’il est difficile de résoudre des problèmes de cette forme en utilisant seulement des expressions régulières.
+
+
† Je sais que j’ai utilisé une syntaxe HTML dans mon exemple. Mais dans l’utilisation réelle que j’en ai faite, je devais matcher entre en: et ::, sachant que parfois les chaines pouvaient se terminer par e::.
Assez bizarrement, je n’ai trouvé aucun outil UNIX pour découper un fichier par mot clé. Alors j’en ai fait un en awk. Je le met ici principalement pour moi, mais ça peut toujours servir à quelqu’un d’autre. Le code suivant découpe un fichier pour chacune de ses ligne contenant le mot UTC.
Les expressions régulières sont très utiles. Cependant, elles ne sont pas toujours la meilleure manière d’aborder certain problème autour des chaines de caractères. Et surtout quand les transformations que vous voulez accomplir sont simples.
+
Je voulais savoir comment récupérer le plus vite possible l’extension d’un nom de fichier. Il y a trois manière naturelle d’accomplir celà :
+
+
# regexp str.match(/[^.]*/); ext=&
+
split
+
ext=str.split(‘.’)[-1]
+
File module
+ext=File.extname(str)
+
+
A première vue, je pensais que l’expression régulière serait plus rapide que le split parce qu’il pouvait y avoir plusieurs de . dans un nom de fichier. Mais la majorité du temps il n’y a qu’un seul point par nom de fichier. C’est pourquoi j’ai réalisé que le split serait plus rapide. Mais pas le plus rapide possible. Il y a une fonction qui est dédiée à faire ce travail dans un module standard de ruby ; le module File.
+puts “Get extname” Benchmark.bm do |x| x.report(“regexp:”) { n.times do str=tab[rand(4)]; str.match(/[^.]*/); ext=&; end } x.report(" split:“) { n.times do str=tab[rand(4)]; ext=str.split(‘.’)[-1] ; end } x.report(” File:") { n.times do str=tab[rand(4)]; ext=File.extname(str); end } end
+
+
Et voici les résultats :
+
+Get extname
+ user system total real
+regexp: 2.550000 0.020000 2.570000 ( 2.693407)
+ split: 1.080000 0.050000 1.130000 ( 1.190408)
+ File: 0.640000 0.030000 0.670000 ( 0.717748)
+
+
En conclusion, les fonction dédiées sont meilleures que votre façon de faire (la plupart du temps).
+puts “remove extension” Benchmark.bm do |x| x.report(" File:“) { n.times do str=tab[rand(4)]; path=File.expand_path(str,File.basename(str,File.extname(str))); end } x.report(”chomp:") { n.times do str=tab[rand(4)]; ext=File.extname(str); path=str.chomp(ext); end } end
+
+
et voici les résultats :
+
+remove extension
+ user system total real
+ File: 0.970000 0.060000 1.030000 ( 1.081398)
+chomp: 0.820000 0.040000 0.860000 ( 0.947432)
+
+
En conclusion du ce second benchmark. Un fonction simple est meilleure que trois fonctions dédiées. Pas de surprise, mais c’est toujours bien de savoir.
git clone peut seulement récuper la branche master.
+
Si vous n’avez pas beaucoup de branches, vous pouvez simplement les clone le project et ensuite pour chacune d’entre elle lancer la commande suivante :
Si vous avez beaucoup de branches il peut être utile d’utiliser le script/la longue ligne de commande suivant(e) :
+
+
# first clone your project $ git clone git@github.com:yogsototh/project.git
+
copy all branches
+$ zsh $ cd project $ for br in $( git br -a ); do case $br in remotes/) print $br ; case ${br:t} in master|HEAD) continue ;; ) git branch –track ${br:t} $br ;; esac ;; esac done
+
+
Et toutes les branches seront récupérées en local.
Voici une solution pour conserver des branches divergentes avec git. Parce qu’il est facile de merger par erreur, je propose un script qui encapsule le comportement de git pour interdire certains merges dangereux. Mais qui permet aussi de faire des merges en cascades de la racines vers les autres branches.
+
Se prémunir contre les erreurs
+
Je travaille sur un projet dans lequel certaines de mes branches git doivent rester divergentes. Et les divergences devraient aller en s’accentuant.
+
J’utilise aussi certaines branches qui contiennent la partie commune de ces projets.
+
Disons que j’ai les branches :
+
+
master: commune à toutes les branches
+
dev: branche instable pour le développement
+
client: Branche commune à plusieurs clients
+
clientA: le projet spécialisé pour le client A
+
clientB: le projet spécialisé pour le client B
+
+
Voilà comment je souhaiterai que ça fonctionne
+
+
+
+
Et plus précisément la hiérarchie des branches :
+
+
+
+
Une flèche de A vers B signifie que l’on peut merger A dans B. S’il n’y a pas de flèche de A vers B cela signifie qu’il est interdit de merger A dans B. Voici le code ruby correspondant :
:master => [ :dev, :client ] signifie que l’on peut merger la branche master dans la branche dev et la branche client.
+
Je fait une erreur si je tape git checkout master && git merge clientA. C’est pour éviter ça que j’ai fait un script pour encapsuler le comportement de git.
+
Mais ce script fait bien plus que ça. Il fait des merges en cascade de la racine vers les feuilles avec l’acion allmerges.
+
+ git co dev && git merge master git co client && git merge master git co clientA && git merge client git co clientB && git merge client
+
+
Je peux ainsi mettre à jour toutes les branches. L’algorithme ne boucle pas même s’il y a des cycles dans la structure des branches.
+Le voici :
+
+
#!/usr/bin/env ruby # encoding: utf-8
+
architecture
+
+
master <-> dev
+
master -> client
+
clien -> clientA | clientB
+
+
merge using two of these branches should be
+
restricted to these rules
+
merge to one of these branch and an unknown one should
+
raise a warning, and may the option to add this new branch
if ARGV.length == 0 puts %{usage: $0:t [git_command or local_command]
+
local commands: allmerges: merge from top to down} exit 0 end
+
require ‘set’ $known_branches=Set.new $architecture.each do |k,v| $known_branches.add(k) v.each { |b| $known_branches.add(b) } end
+
def rec_merge(branch) if $architecture[branch].nil? return end $architecture[branch].each do |b| if $flag.has_key?(b.to_s + branch.to_s) next end flagname=branch.to_s + b.to_s if $flag.has_key?(flagname) next end if system %{eng checkout #{b}} if get_current_branch != b puts “Can’t checkout to #{b}” exit 2 end if system %{eng merge #{branch}} $flag[flagname]=true rec_merge(b) else exit 1 end else exit 1 end end end
+
def do_all_merges puts ‘Will merge from father to sons’ current_branch=get_current_branch $flag={} rec_merge(:master) system %{git co #{current_branch}} end
+
def do_merge current_branch=get_current_branch src_branch=ARGV[1].intern puts %{do_merge: #{src_branch} => #{current_branch}} if $known_branches.include?(current_branch) if $known_branches.include?(src_branch) if $architecture.has_key?(src_branch) and $architecture[src_branch].include?(current_branch) system %{git merge #{src_branch}} else puts %{Forbidden merge: #{src_branch} => #{current_branch}} end else puts %{Warning! #{src_branch} not mentionned in rb configuration} sleep 2 f system %{git merge #{src_branch}} puts %{Warning! #{src_branch} not mentionned in rb configuration} end end end
+case ARGV[0] when ‘allmerges’ then do_all_merges when ‘merge’ then do_merge else system %{git #{ARGV.join(’ ’)}} end
+
+
Pour que ça fonctionne il vous suffit de copier eng dans un répertoire présent dans votre PATH.
+
Bien entendu, il faut essayer de faire aussi peu que possible des cherry-pick et des rebase. Ce script a pour but de s’intégrer avec les workflow basé sur les pull et merge.
…plus on retarde quelque chose, plus il devient difficile de s’y mettre… {: cite=“http://www.madore.org/~david/weblog/2010-05.html#d.2010-05-12.1752” }
+
+
Je devais écrire d’autres articles pour ce blog. J’ai noté plein d’idées dans mes todolist. Mais j’avais pas mal d’autres choses à faire. Et jusqu’ici, j’ai toujours dit «je le ferai plus tard». Ce qui m’a fait agir, c’est la petite réflexion que j’avais lu une fois. > Arrétez d’écrire des TODO dans votre code est faites le maintenant !
+> Vous serez surpris de l’efficacité de cette mesure.
+
En résumé : > Just do it! ou Juste fait le comme auraient dit les nuls.
+
Finallement j’écrirais plus souvent ces derniers temps.
J’ai aussi fini d’adapter mon site à la dernière version de nanoc. La partie difficile étant d’avoir un système qui permet de gérer plusieurs langues. Je pense que j’écrirai les détails dans un article.
+
J’ai aussi une vrai vie. J’ai passé quelques moments agréables avec ma famille et des amis.
+
J’ai travaillé avec luc sur un framework REST/JSON orienté API écrit en ruby. Il fonctionne assez bien, avec très peu de bug jusqu’ici. Nous espérons pouvoir écrire un tutoriel de todolist. Peut-être en deux ou trois posts. Ce framework n’est pas encore public. Il le deviendra certainement lorsque nous aurons fini d’écrire une application web et que nous aurons fait un joli site pour lui.
+
Finallement voilà un partie des choses que je dois faire :
+
+
finir de faire un service web public (je pense que ça peut être populaire) ;
+
finir d’écrire l’application iPhone associée ;
+
finir de publier notre framework pour créer des services web ;
Sur ma page d’accueil vous pouvez voir la liste des mes derniers articles avec le début de ceux-ci. Pour arriver à faire ça, j’ai besoin de couper le code XHTML de mes pages en plein milieu. Il m’a donc fallu trouver un moyen de les réparer.
En réalité, ce n’est pas si difficile que celà peut paraître au premier abord. Le secret réside dans le fait de comprendre que l’on n’a pas besoin de conserver la structure complète de l’arbre pour le réparer, mais seulement la liste des parents non fermés.
+
Pour notre exemple, juste après le paragraphe first paragraph nous n’avons qu’à fermer un div pour la classe corps et le XML est réparé. Bien entendu, quand on est dans le cas où un tag est coupé au milieu, on a qu’à remonté juste avant le début de ce tag corrompu.
+
Donc, tout ce que nous avons à faire, c’est d’enregistrer la liste des parents dans une pile. Supposons que nous traitions le premier exemple complètement. La pile passera par les états suivants :
L’algorithme est alors très simple : ~~~~~~ {.html} let res be the XML as a string ; read res and each time you encouter a tag: if it is an opening one: push it to the stack else if it is a closing one: pop the stack.
+
remove any malformed/cutted tag in the end of res for each tag in the stack, pop it, and write: res = res + closed tag
+
return res ~~~~~~
+
Et res contiend le XML réparé.
+
Finallement, voici le code en ruby que j’utilise. La variable xml contient le XML coupé.
Quand la théorie est plus pratique que la pratique
+
+
+
+
+
+
+
tlpl: :
+
+
J’ai essayé de programmer un simple filtre ;
+
J’ai été bloqué pendant deux jours ;
+
J’ai arrêté de penser comme un robot ;
+
J’ai utilisé un papier et un stylo ;
+
J’ai fait un peu de maths ;
+
J’ai résolu le problème en 10 minutes ;
+
Conclusion: Pragmatisme n’est pas : «n’utilisez jamais la théorie».
+
+
+
Résumé (plus long que le tlpl: )
+
Je devais résoudre un problème à mon travail. Au début cela semblait assez facile. J’ai donc commencé à programmer tout de suite. Je suis alors entré dans un cercle infernal d’essais et de réparations. Voilà à quoi ressemblait cet étrange état de boucle infini :
+
+
– Plus que ça a réparer et ça devrait être bon.
+– Très bien, maintenant ça doit marcher.
+– Oui !!
+– Ah mince! J’ai oublié ce détail…
+répéter jusqu'à la mort
+
+
Après deux jours à me prendre pour Sisyphe, je me suis arrêté pour repenser le problème. J’ai pris un stylo et une feuille de papier. Je me suis souvenu de de ce que j’avais appris sur les arbres pendant mon doctorat. Finalement, le problème fut résolu en moins de 20 minutes.
+
Je pense que la leçon à retenir de cette expérience est de se souvenir que la méthodologie la plus efficace pour résoudre ce problème pragamtique était la méthode théorique. Ça ne signifie pas que la méthode théorique est toujours la meilleure, mais en tout cas, il ne faut pas l’écarter.
+
+
L’anecdote
+
Apparemment 90% des programmeurs sont incapable de programmer une recherche binaire sans faire de bug. L’algorithme est pourtant connu et facile à comprendre. Cependant, il est difficile à programmer sans bug. J’ai participé à ce concours. Vous pouvez voir les résultats ici1. J’ai dû faire face à un problème similaire à mon travail. Il paraissait simple au départ. Transformer un xml d’un format à un autre.
À première vue, cela m’a paru simple. J’étais certain de pouvoir y arriver en me fixant les règles suivantes :
+
+
ne pas utiliser xslt ;
+
ne pas utiliser de parseur xml ;
+
résoudre le problème en utilisant un simple script perl
+
+
Vous pouvez essayer si vous le souhaitez. Si vous attaquez ce problème directement en écrivant le programme, ce ne sera certainement pas si simple. Je peux le dire, parce que c’est ce que j’ai fait. Et je dois dire que j’ai perdu une journée de travail complète en m’y prenant de la sorte. En réalité, il y avait pas mal de petits détails dont je ne parle pas qui m’ont induis en erreur et qui m’ont fait perdre encore plus de temps.
+
Pourquoi étais-je incapable de résoudre ce problème si simple en aparence ?
+
Voici comment je m’y suis pris :
+
+
Réfléchir
+
Écrire le programme
+
Essayer le programme
+
Vérifier les résultats
+
Trouver un bug
+
Résoudre le bug
+
Reprendre à l’étape 3
+
+
Il s’agissait d’une méthode de travail standard pour un ingénieur en informatique. L’erreur venait de la première étape. J’ai d’abord pensé à comment résoudre le problème mais avec des yeux d’ingéinieur pragmatique. Je me suis simplement dit :
+
+
Ça à l’air de pouvoir se résouvre avec un petit script de search&replace en perl Commençons à écrire le code maintenant.
+
+
C’est la deuxième phrase qui est complètement fausse. Parce que j’avais mal commencé et que cette méthodologie de travail ne fonctionne pas lorsque l’on part vraiment mal.
+
Réfléchir
+
Après un certain temps, j’ai arrêté de programmer et je me suis dit : «Maintenant, ça suffit !». J’ai pris une feuille et un stylo et j’ai commencé à dessiner des arbres.
+
J’ai commencer par simplifier un peu en enlevant le maximum de verbiage. Tout d’abord en renommant <item name="Menu"> par un simple M par exemple. J’ai obtenu quelque chose comme :
+
+
et
+
+
Puis, je me suis fait la réflexion suivante :
+
Dans les distances d’éditions sur les arbres, chaque opération atomique correspond à un simple search and replace sur mon fichier xml source2. On considère trois opérations atomiques sur les arbres :
+
+
substitution: renommer un nœud
+
insertion: ajouter un nœud
+
délétion: supprimer un nœud
+
+
Une des particularité avec les transformations sur les arbres est celle-ci : supprimer un nœud et tous ses enfants deviendront les enfants du père de ce nœud.
+
Un exemple:
+
+r - x - a
+ \ \
+ \ b
+ y - c
+
+
Si vous supprimez le nœud x, vous obtenez
+
+ a
+ /
+r - b
+ \
+ y - c
+
+
Et regardez ce que ça implique quand on l’écrit en xml :
Par conséquent, s’il existe un transducteur déterministe à un état qui permet de transformer mes arbres ; je suis capable de transformer le xml d’un format à l’autre en utilisant une simple liste de search and replace.
+
Solution
+
Transformer cet arbre :
+
+R - C - tag1
+ \ \
+ \ tag2
+ E -- R - C - tag1
+ \ \ \
+ \ \ tag2
+ \ E ...
+ R - C - tag1
+ \ \
+ \ tag2
+ E ...
+
+
en celui-ci :
+
+ tag1
+ /
+M - V - M - V - tag2 tag1
+ \ /
+ M --- V - tag2
+ \ \
+ \ M
+ \ tag1
+ \ /
+ V - tag2
+ \
+ M
+
+
peut-être fait en utilisant le transducteur déterministe à un état suivant:
+
+
C -> ε
+E -> M
+R -> V
+
+
Ce qui peut-être traduit par les simples directives Perl suivantes :
Je traduis la plupart de mes articles pour qu’ils soient disponibles en français et en anglais. La façon que l’on m’a conseillé était d’avoir un fichier par langue. En général ça donne ça.
+
+Bonjour,
+
+voici un exemple de texte en français.
+[image](url)
+
+
+Hello,
+
+here is an example of english text.
+[image](url)
+
+
Cette façon de traduire vous impose une certaine façon de traduire. D’abord écrire entièrement le texte dans une langue, puis copier le fichier et enfin retraduire dans une nouvelle langue.
+
Le problème, c’est que très souvent, les articles ont des parties communes non négligeables. Par exemple, les images, les codes sources, etc… Lorsque je m’aperçoit que j’ai fait une erreur dans ces parties communes ça m’oblige à refaire deux fois la même manipulation. Sauf que comme il m’arrive d’être distrait, il peut y avoir pas mal d’aller-retours.
+
C’est pourquoi, j’ai plutôt opté pour une autre solution. J’utilise des tags sur un seul fichier. En fin de compte, mes fichiers ressemblent à :
+
+ fr: Bonjour,
+ en: Hello,
+
+ en: here is an example of english text.
+ fr: voici un exemple de texte en français.
+[image](url)
+
+
Comme j’édite mes fichier avec vim, il m’est très facile d’ajouter ces fr: ou en: en début de ligne à l’aide du très utile C-v. Par contre nanoc a été conçu pour être utilisé par une seule langue. Précédemment, j’avais utilisé les capacité de nanoc pour séparer les langues. Mais finalement, il s’avère bien plus simple de faire un pré-traitement qui nettoie mes fichiers et en fait deux copie qui seront ensuite gérées par nanoc.
+
Vous pouvez récupérer les sources de mon blog (sans tous les articles) à l’adresse suivante github.com/yogsototh/Scratch. J’écrirais un article pour savoir comment l’utiliser facilement. J’ai en effet ajouté beaucoup de scripts et de librairies.
J’ai publié une version light de mon système de blog hier soir. Par light il faut comprendre avec un CSS plus épuré et plus portable (sans les bords ronds). Vous pouvez le récupérer sur github.com.
Now your website reside into the output directory.
+
+
Documentation
+
Useful things to know
+
Multi-language
+
All files in multi are processed and copied in the content directory. For each file in multi, each line starting by ‘fr:’ are copied (without the fr: into the content/html/fr/ tree, but not into the content/html/en tree. File not starting by fr: or en: are copied in each destinations.
+
If you want to add another language, you’ll have to modify tasks/config, and config.yaml, create a content/html/xx where xx is the language code.
+
Edition & Rendering
+
additional keywords
+
You can separate multi content div using the: n``ewcorps directive (see examples).
+
You can create div using b``egindiv(classname), e``nddiv. (See some existing blog entries for example). Use the class intro for the abstract part.
+
You can create nice description table using <``desc> (See source code for example).
+
Typography
+
In French all ‘:’, ‘;’, ‘!’ and ‘?’ are preceded automatically by  . This enable not to have a line starting by a single special character.
+
You can use small caps using <sc> tags.
+
+
(c``) is replaced by (c).
+
(r``) is replaced by (r).
+
<``- is replaced by <-.
+
-``> is replaced by ->.
+
+
source code
+
To write source code you should use the following format:
+
~~~~~~ {.html} ~~~~~~ {.ruby} The code ~~~~~~
+
The file attribute is not required.
+
blog
+
If you want to make really long blog post, you can separate them into many files. To accomplish that, you simply have to make your files like:
All files are intended to be generated into the output/Scratch directory. This was made like that to work nicely with iWeb organisation of websites.
+
menu
+
The order of post is done using the menupriority meta-data in the header of the files.
+
You can hide some file from the menu by setting: isHidden: true in the header.
+
Details
+
To know more about this blog engine, you should look at nanoc project.
+
Then look at the files inside your project:
+
README.md : readme for the project (used by github) :: latest.md : symbolic link to the last blog entry :: multi/ : Directory containing multi-language articles :: tasks/ : scripts for website live :: config.yaml : global configuration file :: Rules : generation rules :: content/ : content files processed by nanoc :: layouts/ : erb templates :: lib/ : ruby libraries used to process files :: output/ : website :: Rakefile : not mandatory for this blog ::
Voici un moyen très simple de ne plus être comptabilisé dans les visites de son propre site. Tout d’abord, vous devriez jeter un coup d’œil sur comment je gère les systèmes de récupération de statistiques. Je centralise tout dans un seul fichier javascript ce qui facilite le travail.
+
Cette méthode nécessite l’utilisation de jquery-cookie.
+
Avant de comptabiliser les visites, je vérifie que la clé admin n’est pas utilisée dans mes cookies.
Maintenant en accédant à ces fichiers depuis votre navigateur vous pouvez disparaître des systèmes d’analyses ou bien être considéré comme tous les autres individus. Pensez à accéder à ces fichiers depuis tous les navigateurs que vous utilisez et vos visites ne seront plus comptabilisées.
Au chargement de la page je crée un div grand comme toute la page avec un fond légèrement transparent que je cache. Je fais bien attention à son z-index pour qu’il soit devant tout le reste.
+
Puis lorsque l’on clique sur un div de class code, je recopie le contenu de celui-ci dans le grand div que je rend visible. Très simple mais très efficace. Pas besoin d’utiliser un plugin jQuery.
J’ai essayé de faire une version de YPassword en jQuery et avec Cappuccino.
+
Cappuccino est très bien sur les navigateurs non mobile mais l’application pèse 1.4Mo et n’est pas compatible avec l’iPhone.
+
la version jQuery n’est pas aussi jolie que la version réalisée avec Cappuccino mais elle pèse seulement 106Ko et est compatible avec l’iPhone.
+
J’essayerai Dashcode 3
+
+
+
+
+
Avant de commencer, je dois dire que je sais que Cappuccino et jQuery ne sont pas plus comparable que Cocoa et la standard library en C++. L’un est fait pour créer des interfaces utilisateurs tandis que l’autre est plus une librairie qui aide aux tâches de bas niveaux. Par contre je les ai utilisé tous les deux pour faire la même application. C’est pourquoi je compare l’expérience que j’ai retenu de chacun pour cette tâche.
+
+
J’ai fait une version web de mon widget YPassword. C’est un simple widget qui permet d’organiser ses mots de passes simplement avec une grande sécurité et de façon portable. Ce n’est pas un widget créé pour remplacer le trousseau d’accès, mais plus un générateur de mots de passe.
+
Le premier a été élaboré à partir du code de mon widget Mac. Vous pouvez l’essayer ici. J’ai ensuite fait une version avec Cappuccino, que vous pouvez essayer ici.
+
Que fait ce widget ?
+
+
Si vous vous moquez de savoir ce que fait mon widget, vous pouvez allez directement à la section suivante.
+
+
J’organise mes mots de passe avec une méthode simple. Je mémorise un mot de passe maître. Et mon mot de passe est alors (principalement) : hash(motDePasseMaitre+NomDeDomaine)
+
En réalité j’ai besoin d’un plus d’informations pour créer mon mot de passe :
+
+
Un mot de passe maître ;
+
une URL ;
+
une longeur maximale de mot de passe ;
+
le type de sortie (hexadécimale ou base64) ;
+
Combien de fois mon mot de passe a dû être changé.
+
+
Le mot de passe résultant est calculé comme suit :
En fait, selon le site web, on peut avoir des contraintes très différentes :
+
+
longueur minimale ;
+
longueur maximale ;
+
ne doit pas contenir de caractères spéciaux ;
+
doit contenir des caractères spéciaux ;
+
etc…
+
+
Et si vous souhaitez changer votre mot de passe, le nombre de changement sert à ça. Toutes les informations peuvent rester publiques sans trop de danger à l’exception du mot de passe principal.
+
Si vous souhaitez avoir encore plus de détails vous pouvez toujours lire certaines de mes anciens articles de blog (en anglais) :
Tout d’abord je voudrais dire que les applications réalisées avec Cappuccino sont tout simplement incroyables. C’est comme avoir une application Mac dans son navigateur.
+
Je dois aussi admettre que j’ai pris du plaisir a écrire mon application avec Cappuccino. C’est comme programmer une application Mac ou iPhone. Si vous connaissez bien Cocoa, vous vous sentirez comme à la maison. Si vous ne connaissez pas Cocoa, je vous conseille de vous y intéresser. Il s’agit vraiment d’un framework excellent pour faire des interfaces utilisateur. Je ne suis pas un spécialiste de tous les frameworks. Mais j’ai réalisé des Interfaces Utilisateurs avec les MFC, Java Swing1 et WXWindows il y a quelques années. Et je dois dire que Cocoa est bien meilleurs que tous ces framework.
+
Cappuccino est un framework spécialisé dans le développement d’application web vraiment exceptionnel. Mais il a aussi quelques défauts qui ont surgit lors de l’écriture de mon widget.
J’ai mis un bon moment avant de comprendre comment récupérer le onChange des champs textuels.
+
La documentation manquait d’organisation.
+
Ça ne marche pas sous iPhone.
+
Il a fallu déployer 11Mo.
+
Il faut télécharger 1,3Mo pour que l’application se charge dans le navigateur.
+
+
Je n’ai pas utilisé les bindings parce qu’il me semble qu’ils ne sont pas prêts.
+
jQuery
+
La version jQuery de YPassword n’est pas aussi bien finie que celle de Cappuccino. Simplement parce qu’il n’y a pas de slider directement avec jQuery. Il faudrait que j’utilise jQueryUI. Et je pense que l’application deviendrait beaucoup plus lourde pour le coups. En tout cas largement au dessus des 106Ko actuels.
+
J’ai utilisé le code de mon widget mac en l’adaptant un peu pour faire cette version. C’était relativement facile. Mais jQuery n’est pas un framework orienté application. Il s’agit plus d’un framework pour faire des animations qui la pète.
+
Je n’ai pas beaucoup plus à dire sur la version jQuery, sinon que programmer avec jQuery était de la programmation de niveau beaucoup plus bas qu’avec Cappuccino.
+
En conclusion
+
Si vous voulez faire une application compatible iPhone n’utilisez pas Cappuccino. Du moins pas encore. Si vous souhaitez faire un application très simple (comme la mienne), je pense que Cappuccino est un peu trop lourd pour ça.
+
Si vous souhaitez faire des applications web complexes qui ressemblent à des applications de bureau alors clairement Cappuccino est un très bon choix. Notez cependant qu’il peut être un peu difficile de débuter.
+
Finallement, pour terminer la version web de mon widget, j’essayerai Dashcode 3. Il semblerai que ce soit une bonne alternative pour créer des widget sur le web compatible iPhone. Je ne sais pas si les applications réalisées avec Dashcode 3 sont compatibles pour les browser n’utilisant pas webkit. Mais si c’est le cas, alors ça pourrait sonner le glas des projets comme Cappuccino et Sproutcore.
+
+
+
+
Si ça vous intéresse vous pouvez jeter un coup d’œil à SEDiL. Je suis assez fier de la vue automatique des arbres que j’ai programmé sans librairie de départ.↩
Beaucoup d’utilisateurs de Reddit m’ont rapporté que mon site était très long à charger et à scroller. Ils pensaient qu’il s’agissait d’un problème dû aux ombres que j’applique sur le texte. J’étais un peu surpris puisque je fais mes tests sur une machine vraiment très lente et je n’avais jamais détecté ces problèmes. En réalité, ce qui ralenti le rendu de ce site est par ordre d’importance :
+
+
Les dégradés sur Chrome (pas dans Safari sur Mac)
+
les box shadows sur Firefox
+
+
les dégradés
+
Sur Safari il n’y a absolument aucun problème avec les dégradés. Par contre sur Chrome sous Linux le site devient quasiment inutilisable.
+
Safari et Chrome utilisent webkit tous les deux. Lorsque vous accéder à ce blog avec javascript activé, un CSS spécifique à votre navigateur est ajouté. Jusqu’à maintenant je faisais un tri entre : IE, Mozilla et Webkit. Maintenant j’ai rajouté un cas particulier pour Chrome. Maintenant j’ai supprimé les gradients lorsque vous naviguer sur ce site en utilisant Chrome.
+
Je n’ai pas vérifier la vitesse de rendu de toutes les propriétés de CSS 3. Mais je vous conseille de ne pas utiliser -webkit-gradient avec Chrome. Au moins sous Linux.
+
Les ombres (box-shadow)
+
J’ai aussi remarqué que -moz-box-shadow ralenti le rendu sous Firefox (et sous Linux). Alors que l’équivalent webkit ne pose aucun problème à Safari sous Mac.
+
Ombres de texte
+
Beaucoup d’utilisateurs mon dit d’utiliser text-shadows avec parcimonie. Mais je pense qu’il ne s’agissait pas là du problème du ralentissement du site. C’est pourquoi je vais les remettre.
+
en conclusion
+
N’utilisez pas -webkit-gradient avec google Chrome pour l’instant. Utilisez -moz-box-shadow avec parcimonie.
tlpl: Je crée un mode mathématique simple pour parler de différents types d’indécidabilités :
+
+
indécidabilité due aux erreurs d’observation ;
+
grandes erreurs résultant de petites erreurs de mesure ;
+
indécidabilité fractales ;
+
indécidabilité logique.
+
+
+
+
Les indécidabilités
+
+
Si le monde a été fabriqué par un démiurge, on peut dire que celui-ci devait avoir le sens de l’humour. Et le récit que je vais faire va vous en fournir la preuve. Je vais me mettre à sa place. Je vais créer un monde simplifié. Un monde régi exactement par nos règles mathématiques. Puis je vais vous parler du mal qui touche cet Univers semblable au notre ; l’indécidabilité. L’incapacité de savoir si nous avons trouvé la vérité, ou seulement une approximation de celle-ci. L’incapacité de prédire certaines choses qui semblent pourtant aller de soi. Voilà comment tout aurait pu commencer.
+
+
+
+
+
Au début, il n’y avait rien. Puis un article de blog commença à prendre forme. J’inspire profondément pour sentir la pesanteur de ce que je vais accomplir. Attention, une dernier moment de tension et je crée l’Univers. Un Univers qui n’existera que le temps de la lecture de cet article. Me voici le démiurge de cet Univers et te voilà son observateur privilégié.
+
Comme j’aime bien tout contrôler, je fabrique ce monde avec quelques règles simples. Je décide que les vrais règles de ce monde sont celles que nous pensons qui régissent notre monde. Notez qu’il y a une grande différence. Pour leur monde, ce que l’on croit vrai aujourd’hui, est vraiment vrai pour eux. Leur monde est donc plus simple à priori que le notre. En particulier, on peut le décrire avec des axiomes et des règles mathématiques. Alors qu’il est possible que ce ne soit pas le cas de notre Univers. Mais nous reviendront là-dessus plus tard.
+
Bon au travail maintenant, je crée une Terre. J’y ajoute des habitants intelligents, les Ys. Bien entendu ils se posent des questions. En particulier, ils se demandent quelles sont les lois qui régissent leur monde. Ils pensent que connaître toutes ces règles leur permettrait de connaître l’avenir. Leur naïveté est touchante. Ah, si seulement ils savaient. Mais je suis là pour les aider à apprendre.
+
Comme je suis un Dieu un peu facétieux, je vais leur jouer quelques tours. Sinon on s’ennuierai à mourir. Le premier est de leur donner des sens imparfaits. De plus il leur est impossible d’avoir des mesures parfaites. Je leur laisse cependant toutes libertés pour améliorer leur technologie et diminuer ces erreurs de mesures.
+
Les habitants de ce monde pensent que celui-ci est plat. Certains d’entre eux pensent qu’il est possible de découvrir les règles du monde que j’ai créé. Et bien que le jeu commence.
+
Commençons par leur première leçon, les erreurs causent de l’indécidabilité.
+
Indécidabilité dues aux erreurs de mesures
+
Voici ce que pense l’un de ces individus.
+
+
Tous les triangles que j’observe semble avoir une propriété commune. La somme de leurs angles est toujours π radiants (180°). Il s’agit certainement d’une loi de mon Univers. Mais comment être certain que tous les triangles de mon Univers possèdent cette propriété ?
+
+
+
+
+
Certain d’entre eux commencent à formaliser un petit peu le problème et ils finissent faire une preuve mathématique. Magnifique ! La preuve est correcte, mais il reste un petit problème. La preuve s’appuie sur des axiomes et des règles. Comment être certain que ces règles et ces axiomes sont vrai dans leur monde? Ils auront beau faire des mesures de plus en plus précises qui conforteront cette formule, ils n’auront que l’espoir et jamais la certitude que la formule est vrai. Simplement parce que le seul moyen de vérifier la véracité des axiomes est par l’observation. Hors en tant que dieu facétieux, j’ai interdit les observation avec des mesures parfaites.
+
Bien entendu, ils prient, ils m’appellent à l’aide. Et comme tout Dieu qui se respecte, je ne réponds pas. Ah ah ah ! J’ai toujours aimé faire ce genre de chose. Ensuite je ferai comme si je n’existe pas. Encore un bonne blague !
+
Si certains se sentent accablés, il leur reste un espoir :
+
+
Espoir
+
Si nous faisons de faibles erreurs de mesure, nous aurons de faibles erreurs dans nos prédictions.
+
+
Indécidabilité avec erreurs croissantes
+
+
+
+
Malheureusement pour eux, il y a le problème des 3 corps. Prenons les formules de la gravitation Universelle et appliquons la à deux corps célestes. Si on connait la position de ces corps avec un grande précision, on pourra aussi connaître la position future de ces corps avec une grande précision. L’hypothèse selon laquelle de petite erreurs de mesures impliquent de petites erreurs prédictive est confortée. Cependant, il y a un problème. Reprenons le même problème mais avec trois corps. Par exemple, avec le Soleil, la Terre et la Lune. Dans ce cas, les erreurs de mesures initiales vont s’amplifier. S’amplifier au point de rendre toute prédiction inutilisable.
+
Là encore une voix d’espoir s’élève :
+
+
Peut-être pouvons nous calculer l’erreur maximale acceptable pour prédire quelque chose. Et nous pourrions au moins savoir ce que nous pouvons prédire ou pas.
+
+
Une fois encore, ça ne va pas très bien se passer.
+
Indécidabilité fractale
+
Considérons la question suivante :
+
+
+
+
Soit des coordonnées GPS précises à 1m près. Les coordonnées sont proches des côtes de la Bretagne. Ce point va-t-il tomber dans la mer ou sur la terre ferme ?
+
Et bien, pour certaines coordonnées, c’est impossible de le savoir. Même si je réduis l’erreur à une valeur infinitésimale. Simplement parce que certains voisinages autour d’un point contiennent toujours à la fois de l’eau et de la terre. Et ce quelque soit la taille du voisinage.
+
On peut même imaginer une structure ou tous les points sont au bord de celle-ci, on ne peut donc pas se permettre d’erreur1.
+
Mais que vois-je ? Un petit malin essaye de trouver la vérité en s’extrayant de mon Monde et en faisant un article sur un blog ? Ça ne va pas se passer comme ça ! Croyez moi ! > Faire des prédictions précises à partir des données observées semble être une quête vouée à l’échec. > Mais je suis persuadé que l’on peut aller au delà. > Au diable ce Dieu qui nous empêche d’avoir des mesures précises ! > Inventons notre propre Univers mathématique. > Un monde qui se suffit à lui-même. > Un monde dans lequel il n’y aura plus d’erreur de mesure. > Un monde entièrement contrôlé par des règles que nous aurons choisi. > Un monde similaire au notre mais où tout pourra être prédit.
+
Indécidabilité logique
+
+
+
+
Jusqu’ici, tous les problèmes d’indécidabilités étaient dûs aux erreurs. Maintenant peut-être que privé d’erreur de mesure, on pourrait enfin résoudre tous les problèmes.
+Et bien non. Même dans un monde mathématique complètement contrôlé. On peut créer un objet pour lequel on ne pourra pas décider à l’avance ce qu’il fait.
+
Il s’agit du problème de l’arrêt.
+
Le Théorème stipule qu’il n’existe pas de programme permettant de décider si un autre programme s’arrête. La preuve est suffisamment simple pour rentrer dans ce post, donc je me fais un petit plaisir en la donnant.
+
+
Supposons qu’il existe un programme qui puisse dire si un autre programme s’arrête. Plus précisément :
+
Hypothèse: Il existe un programme P tel que:
+
+
P(x,y) réponde “s’arrête” en un temps fini si et seulement si x(y)2 s’arrête effectivement en temps fini et
+
P(x,y) réponde “ne s’arrête pas” en un temps fini dans le cas contraire.
+
+
Remarque: Tout code de programme est une chaîne de caractère qui peut être utilisée aussi comme entrée d’un autre programme. Ainsi écrire P(x,x) est autorisé.
+
Soit le programme Q que j’écris comme suit :
+
+Q(x) :
+ si P(x,x)="s'arrête" alors je fais une boucle infinie.
+ si P(x,x)="ne s'arrête pas" alors je m'arrête.
+
+
Maintenant que répond P(Q,Q)?
+
+
si P(Q,Q) répond “s’arrête” ça implique que P(Q,Q)=“ne s’arrête pas”
+
si P(Q,Q) répond “ne s’arrête pas” ça implique que P(Q,Q)=“s’arrête”
+
+
Il y a donc une contradiction que le seul moyen de régler est par la non existence du programme P.
+
+
C’est simple, je suis le démiurge de ce monde imaginaire. Et même moi, je dois me soumettre à cette règle. Comme quoi, avoir la possibilité de créer le monde et la toute puissance sont deux choses différentes.
+
+
Après tout ceci, il peut sembler difficile de savoir en quoi nous pouvons croire. Mais ce serait une erreur de jeter le bébé avec l’eau du bain. Dans une seconde partie, j’expliquerai ce que nous pouvons espérer et qu’elle attitude nous devons adopter une fois que l’on a réalisé que beaucoup de vérité nous sont inaccessibles.
Avant les vacances beaucoup d’utilisateurs se sont plaints de la lenteur de rendu de mon site. Il s’agit notamment de problèmes avec Chrome en particulier. Mais pour éviter tout problème. J’ai complètement modifié le style de mon site web. Il est inspiré du style de l’application iBooks(c) sur iPhone(c).
+
Dites moi ce que vous pensez de ce nouveau design.
J’ai changé mon hébergeur. Mobileme n’est absolument pas adapté à la diffusion de mon blog. C’est pourquoi je suis passé à Heroku.
+
Mais comme vous devez le savoir mon blog est un site complètement statique. J’utilise nanoc pour l’engendrer. Avoir un site statique amène beaucoup d’avantages par rapport à un site dynamique. Surtout en terme de sécurité. Voici comment configurer un site statique sur heroku.
+
La racine de mes fichiers est ‘/output’. Vous devez simplement créer deux fichiers. Un fichier config.ru1 :
J’ai dû envoyer un mail en ligne de commande récemment. Quelle ne fût pas ma surprise lorsque je constatais que ce n’était vraiment pas évident. Je n’avais ni pine ni mutt. Seulement mail et mailx.
+
Ce qu’on trouve sur internet pour envoyer un mail avec fichier attaché c’est ça :
Bon, alors, bête et discipliné j’ai essayé. Et bien, ça marche presque tout le temps. Pour mon fichier ça n’a pas marché du tout. Je l’ai compressé au format .gz, .bz2 et .zip. Avec le format .bz2 le mail reçu avait bien un fichier attaché. Mais avec les formats .gz et .zip, ça ne fonctionnait pas. Au lieu d’avoir un fichier attaché j’avais un message qui contenait quelque chose comme :
+
+begin 664 fic.jpg
+M(R$O=7-R+V)I;B]E;G8@>G-H"GAL%]M8UPB/BD\=F%L
+M=64O/B@\+VET96T^*2-<)#$\=F%L=64^)&ME>7=O
+
+Pas très lisible.
+Après pas mal de recherche j" ai trouvé la solution. Le problème c'est `uuencode` qui est une méthode qui devrait devenir obsolète pour envoyer les fichiers. Il vaut mieux utiliser le format MIME pour envoyer des fichiers attachés. Donc finalement le mieux est de faire ça "à la main" avec `sendmail`. Je n'ai quand même pas utilisé `telnet`. La commande à lancer est : ~~~~~~ {.zsh} sendmail -t -oi < mailcontent.txt ~~~~~~ Bien entendu il faut créer le fichier `mailcontent.txt` qui contient :
+From: from@mail.com
+To: to@mail.com
+Subject: View the attached file
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="-"
+
+This is a MIME encoded message. Decode it with "Decoder"
+or any other MIME reading software. Decoder is available
+at .
+---
+Content-Type: image/jpeg; name="fic.jpg"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline; filename="fic.jpg"
+
+H4sICB6Ke0wAA2Rjcl93aXRob3V0X2tleXdvcmQuY3N2ANSdW5ubOJPH7/e7
+7Brw+dmrTk8yk7yTSTaZeWd2b/TIIGy6MRAE7ng+/VaJgwF3g522SsxN2+3T
+/4eOJamqmARP+yibvI8ykUYim+x5EE2euBfIyd3byZ+fvvzr7svbu8ndTx/f
+...
+
+
Et pour avoir le code il suffit de lancer la commande :
+
uuencode -m fic.jpg fic.jpg ~~~~~~
+
Et voilà. Parfois la technique c’est tellement simple. Si j’en ai besoin encore quelques fois, je pense que j’écrirai un émetteur de mail en shell.
Vous pouvez remarquer qu’à la fin de chaque page je donne une date de dernière modification. Précédemment cette date était calculée en utilisant la date du fichier. Mais il arrive fréquemment que je fasse un touch d’un fichier pour engendrer tout le site de nouveau. Donc la date n’est pas nécessairement la vraie de modification du contenue.
+
J’utilise git pour versionner mon site web. Et cet outil me permet de récupérer la dernière date de vraie modification d’un fichier. Voici comment je m’y prend avec nanoc :
Bien entendu je sais que c’est très lent et absolument pas optimisé. Mais ça fonctionne comme prévu. Maintenant la date que vous voyez en bas de la page correspond exactement à la dernière date de modification de son contenu.
+
Mise à jour: Je tiens à remercier Eric Sunshine et Kris pour leurs conseils sur ce problème.
Allons directement à l’essentiel : voici deux fonctions à intégrer à votre application iPhone pour afficher l’encodage en base64 ou en hexadecimal du hash sha1 d’un string en Objective-C pour iPhone.
+
Pour l’usage c’est très simple, copiez le code dans la classe de votre choix. Puis :
Vous avez pu constater que j’ai modifié le design de mon blog. Maintenant il doit être beaucoup plus léger qu’avant. Je n’utilise plus de CSS3 et beaucoup moins de javascript. Bien entendu, même avant, mes pages étaient parfaitement lisibles sans javascript. Mais, je me suis aperçu que les systèmes de CSS3 sont loin d’être au point. J’utilisait des gradient en CSS3, ainsi que des ombres sous le texte. Ça avait un rendu très sympa. Sauf… Ce n’était pas compatible ie6, sous Chrome le rendu était d’une lenteur incroyable. J’ai donc décidé de faire un site à minima. Je voulais qu’il soit joli et le plus simple possible pour assurer sa compatibilité. Les règles que je me suis fixées sont donc:
+
+
pas d’élément CSS qui commence par -moz ou -webkit, etc… ;
+
pas d’ombre sous le texte pour donner une impression de profondeur ;
+
nettoyer pas mal le code et enlever tout ce que je peux ;
utilisez Mail plutôt que l’interface web de gmail.
+
+
+
J’ai (re)découvert comment adoptez la norme S/MIME. J’ai été surpris de voir à quel point ce fut aisé. Il y a seulement quelques années c’était bien plus difficile à accomplir. Maintenant je peux signer et chiffrer mes mails.
+
Pourquoi est-ce important ?
+
Signer : cela permet de certifier avec une absolue certitude que la personne qui a écrit le mail est vous ou au moins qu’elle a utilisé votre ordinateur.
+
Chiffrer : parce que parfois il est nécessaire d’être certain qu’une conversation reste privée.
utilisez Mail plutôt que l’interface web de gmail. Maintenant vous devriez voir ces icônes :
+
+
+
+
+
n.b. : si vous utilisez gmail et que vous ne travaillez pas toujours avec un Mac, vous devriez considérer d’utiliser le module gmail S/MIME de firefox.
tlpl: Je me suis amusé à lire un fichier wav. Le C fut le langage le mieux adapté à ce traitement. Bien meilleur que Ruby par exemple.
+
edit: Je voulais que ce programme fonctionne sur une machine spécifique. En aucun cas je ne pensais publier ce code pour une utilisation autre que celle-ci.
+
+
J’ai eu besoin de calculer la somme des valeurs absolue des données d’un fichier wav. Pour des raison d’efficacité (et aussi de fun), j’ai fait le programme en C.
+
Celà faisait longtemps que je n’avais pas programmé en C. De mémoire il était peu aisé de manipuler des fichiers. Mais je dois concéder que j’ai été étonné de la clarté du code que j’ai obtenu.
+
Tout d’abord, un fichier wav se compose d’un entête qui contient pas mal de meta données. Cet entête a été optimisé pour prendre peu de place. Donc on discute de l’entête avec des nombres d’octets :
+
+
Les 4 premiers octets doivent contenir RIFF en ASCII ;
+
les 4 octects suivant correspondent à un entier codé sur 32 bits qui donne la taille du fichier moins 8 octets. etc..
+
+
Etonnamment je pense que lire ce type de fichier avec un langage de haut niveau aurait été plus pénible qu’en C. La preuve, il m’a suffit de chercher sur le net le format complet de l’entête et de l’écrire dans un struct.
Si j’avais eu à faire ça en Ruby, je pense qu’il m’aurait fallu pour chaque bloc de l’entête écrire un bout de code de lecture du bon nombre d’octets. Alors qu’en C il m’a suffit d’écrire:
Et en une seule étape ma structure de donnée a été remplie avec les valeurs souhaitées. Magique !
+
Ensuite, récupérer un entier à partir de deux octets n’est pas non plus une opération naturelle dans les nouveaux langages de programmation. Alors qu’en C. Pour récupérer un entier codé sur 16 bits il suffit d’écrire :
Bien entendu ce code n’est qu’un hack. Mais on voit bien comment on peut facilement améliorer ce code, ajouter des cas possibles par exemple. Comme je dis souvent : le bon outil pour la bonne tâche. On voit en effet que pour cette tâche C est bien supérieur à Ruby par exemple.
+
_màj : pour des raisons de compatibilité (machines 64 bits) j’ai utilisé int16_t au lieu de short et int au lieu de int.
+
Je serai curieux de savoir s’il existe un manière plus propre en Ruby que je ne connais pas. Certainement qu’en Python ça doit être la cas.
+
+
Màj (2) : après toutes les remarques concernant la portabilité. J’ai fait une nouvelle version qui devrait être plus portable. Elle fait aussi plus de test pour vérifier le fichier. Cependant j’utilise une assertion spécifique à gcc pour être certain que la structure de donnée n’ai pas de “trou” :
tlpl: J’ai fait un système simple de macros pour mon blog. Par exemple, il me suffit d’écrire %latex et ça affiche LaTeX.
+
+
J’ai ajouter un système de macro pour mon système de blog. Lorsqu’on est habitué à LaTeX et que l’on commence à écrire des articles un peu conséquent avec des notations mathématiques, les macros deviennent vite quelque chose d’indispensable.
+
Dans l’entête de mes fichiers j’écris simplement:
+
+
Puis dans le corps ça va remplacer :
+
+
%test par Just a test ;
+
et %latex par LaTeX
+
+
Le code est assez simple. Pour les utilisateurs de nanoc il suffit de copier le fichier suivant dans le répertoire lib.
J’étais très occupé ces derniers mois. Maintenant il me semble que je vais pouvoir faire revivre ce blog.
+
J’ai fait un outil qui permet d’écrire des livre en utilisant une syntaxe proche de markdown. C’est un markdown avec des macros (essentiel pour les textes longs). De plus le système gère la génération de pages HTML ainsi que du PDF engendré avec du XeLaTeX. Je n’en ai pas encore terminé avec ça. Mais si je tarde trop, je communiquerai lorsque j’aurai fini le minimum.
+
J’ai écrit un framework MVC pour application javascript simple mais néanmoins très rapide.
Mise à jour : Je pense que je vais finallement changer d’avis. Pourquoi ? Tout d’abord, je viens de découvrir un convertisseur javascript vers coffeescript, ensuite Denis Knauf m’a laissé un commentaire et m’a appris l’existence d’une fonction CoffeeScript.eval. De plus, il faut voir CoffeeScript comme javascript avec une syntaxe similaire à Ruby et pas comme un langage similaire à Ruby.
+
+
+
tlpl: Qu’est-ce qui n’allait pas avec Coffeescript? La meta-programmation, il faut le “vendre” aux autres, une nouvelle étape de compilation intermédiaire sans fournir les avantages de Cappuccino, la sensation que c’est un peu instable.
Je me suis battu avec l’horrible syntaxe de javascript. C’était comme revenir des années dans le passé :
+
+
une syntaxe à la Java très verbeuse ;
+
une syntaxe follement verbeuse et étrange pour la programmation orientée objet ;
+
pas de manière naturelle de se référer à l’instance d’une classe ;
+
etc…
+
+
J’étais tellement ennuyé par tous ces point qu’il était arrivé un moment où je commençais à vouloir faire mon propre CoffeeScript.
+
J’ai fini une première version de mon framework MVC en javascript et j’ai appris l’existence de CoffeeScript. Merci à git, j’ai immédiatement créé une nouvelle branche dans le seul but d’essayer CoffeeScript.
+
Voici mon expérience :
+
+
J’ai dû installer node.js et utiliser npm simplement pour utiliser CoffeeScript. Ce n’était pas très difficile, mais pas aussi facile que ce que j’aurai aimé.
+
Les fichier javascript existants ne sont pas compatible avec coffee.
+
Il n’y a pas script pour aider à transformer les anciens fichiers javascripts en fichier coffee. Du coups j’ai dû faire ça manuellement. Merci à vim, il ne fut pas très difficile de transformer 90% des fichiers avec des expressions régulières. L’option --watch de coffee était très utile pour debugger cette transformation. Cependant, il m’a fallu écrire mon propre script pour que tous mes fichiers soient watchés dans tous les sous-répertoires.
+
Quelque chose à laquelle je n’avais pas pensé. J’ai fait un peu de meta-programmation en javascript en utilisant eval. Mais pour que celà fonctionne correctement, il faut que la chaîne de caractère que je passe à eval soit codée en javascript et pas en coffee. C’est un peu comme écrire dans deux langages différents au même endroit. Ça ne me parraissait vraiment pas agréable.
+
+
Conclusion
+
Avantages :
+
+
Code plus lisible : CoffeeScript résoud la majorité des problèmes de syntaxes de javascript
+
Concision : j’ai gagné 14% de lignes, 22% de mots et 14% de caractères.
+
+
Inconvénients :
+
+
Ajout d’une nouvelle étape de compilation avant de pouvoir vérifier le comportement de mon site
+
Facilité d’utilisation : il m’a fallu créer un script pour gérer la génératio automatique des fichiers
+
Il faut apprendre un autre langage proche de ruby
+
La meta-programmation devient une expérience désagréable
+
Je dois convaincre les personnes travaillant avec moi :
+
+
d’installer node.js, npm et CoffeeScript ;
+
de se souvenir de lancer un script à chaque session de codage ;
+
d’apprendre un autre language proche de ruby.
+
+
+
Les deux derniers points étant de mon point de vue les plus problématiques.
+
Mais même si j’avais à travailler seul, je n’utiliserai certainement pas CoffeeScript. Il s’agit d’un tier dont la moindre mise à jour pourrait rendre mon code inutilisable. Cette situation m’est déjà arrivée plusieurs fois et c’est très désagrable. Beaucoup plus que coder avec une mauvaise syntaxe.
+
Digression
+
Je suis attristé. J’espérais tant pouvoir programmer Javascript avec une touche de Ruby. En fin de compte, cette solution n’est pas pour moi. Je vais devoir utiliser l’horrible syntaxe javascript pour l’instant. À la limite j’aurai préféré un script Ruby2Js par exemple2. Mais il me semble que ça serait un travail très difficile rien que pour simuler l’accès à la classe courante.
+
Typiquement @x est transformé en this.x. Mais le code suivant ne fait pas ce que j’attendrai de lui.
Sachant celà, la notation @ perd tout son intérêt pour moi.
+
+
+
+
Je sais que ce n’est certainement ni la meilleure ni la plus productive des décisions. Mais j’aime bien fabriquer les choses pour savoir comment tout fonctionne dans le détail.↩
+
Je sais qu’il existe un projet rb2js, mais il ne résoud pas le problème dont je parle.↩
Sometimes, the type determine a lot about the function★:
+
fst :: (a,b) -> a -- Only one choice
+snd :: (a,b) -> b -- Only one choice
+f :: a -> [a] -- Many choices
+-- Possibilities: f x=[], or [x], or [x,x] or [x,...,x]
+
+? :: [a] -> [a] -- Many choices
+-- can only rearrange: duplicate/remove/reorder elements
+-- for example: the type of addOne isn't [a] -> [a]
+addOne l = map (+1) l
+-- The (+1) force 'a' to be a Num.
The couple (F,fmap) is a \(\Hask\)'s functor if for any x :: F a:
+
fmap id x = x
+
fmap (f.g) x= (fmap f . fmap g) x
+
+
+
+
Haskell Functors Example: Maybe
+
+
data Maybe a = Just a | Nothing
+instance Functor Maybe where
+ fmap :: (a -> b) -> (Maybe a -> Maybe b)
+ fmap f (Just a) = Just (f a)
+ fmap f Nothing = Nothing
+
fmap (+1) (Just 1) == Just 2
+fmap (+1) Nothing == Nothing
+fmap head (Just [1,2,3]) == Just 1
+
+
+
Haskell Functors Example: List
+
+
instance Functor ([]) where
+ fmap :: (a -> b) -> [a] -> [b]
+ fmap = map
abusing notations denoting join by ⊙; this is equivalent to (F ⊙ F) ⊙ F = F ⊙ (F ⊙ F)
+
There exists η :: a -> F a s.t. η⊙F=F=F⊙η
+
+
+
+
Klesli composition
+
Now the composition works as expected. In Haskell ◎ is <=< in Control.Monad.
+
g <=< f = \x -> join ((fmap g) (f x))
+
f x = [x] ⇒ f 1 = [1] ⇒ (f <=< f) 1 = [1] ✓
+g x = [x+1] ⇒ g 1 = [2] ⇒ (g <=< g) 1 = [3] ✓
+h x = [x+1,x*3] ⇒ h 1 = [2,3] ⇒ (h <=< h) 1 = [3,6,4,9] ✓
+
+
+
+
We reinvented Monads!
+
A monad is a triplet (M,⊙,η) where
+
+
\(M\) an Endofunctor (to type a associate M a)
+
\(⊙:M×M→M\) a nat. trans. (i.e. ⊙::M (M a) → M a ; join)
+
\(η:I→M\) a nat. trans. (\(I\) identity functor ; η::a → M a)
+
+
Satisfying
+
+
\(M ⊙ (M ⊙ M) = (M ⊙ M) ⊙ M\)
+
\(η ⊙ M = M = M ⊙ η\)
+
+
+
+
Compare with Monoid
+
A Monoid is a triplet \((E,∙,e)\) s.t.
+
+
\(E\) a set
+
\(∙:E×E→E\)
+
\(e:1→E\)
+
+
Satisfying
+
+
\(x∙(y∙z) = (x∙y)∙z, ∀x,y,z∈E\)
+
\(e∙x = x = x∙e, ∀x∈E\)
+
+
+
+
Monads are just Monoids
+
+
A Monad is just a monoid in the category of endofunctors, what's the problem?
+
+
The real sentence was:
+
+
All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
+
+
+
+
Example: List
+
+
[] :: * -> * an Endofunctor
+
\(⊙:M×M→M\) a nat. trans. (join :: M (M a) -> M a)
+
\(η:I→M\) a nat. trans.
+
+
-- In Haskell ⊙ is "join" in "Control.Monad"
+join :: [[a]] -> [a]
+join = concat
+
+-- In Haskell the "return" function (unfortunate name)
+η :: a -> [a]
+η x = [x]
A LOT of monad tutorial on the net. Just one example; the State Monad
+
DrawScene to State Screen DrawScene ; still pure.
+
main = drawImage (width,height)
+
+drawImage :: Screen -> DrawScene
+drawImage screen = do
+ drawPoint p screen
+ drawCircle c screen
+ drawRectangle r screen
+
+drawPoint point screen = ...
+drawCircle circle screen = ...
+drawRectangle rectangle screen = ...
+
main = do
+ put (Screen 1024 768)
+ drawImage
+
+drawImage :: State Screen DrawScene
+drawImage = do
+ drawPoint p
+ drawCircle c
+ drawRectangle r
+
+drawPoint :: Point ->
+ State Screen DrawScene
+drawPoint p = do
+ Screen width height <- get
+ ...
+
+
+
fold
+
+
+
+
κατα-morphism
+
+
+
+
κατα-morphism: fold generalization
+
acc type of the "accumulator": fold :: (acc -> a -> acc) -> acc -> [a] -> acc
Algebra representing the (+1) and also knowing about the 0.
+
+
First example, make length on [Char]
+
+
+
κατα-morphism: Type work
+
+data StrF a = Cons Char a | Nil
+data Str' = StrF Str'
+
+-- generalize the construction of Str to other datatype
+-- Mu: type fixed point
+-- Mu :: (* -> *) -> *
+
+data Mu f = InF { outF :: f (Mu f) }
+data Str = Mu StrF
+
+-- Example
+foo=InF { outF = Cons 'f'
+ (InF { outF = Cons 'o'
+ (InF { outF = Cons 'o'
+ (InF { outF = Nil })})})}
+
+
+
+
κατα-morphism: missing information retrieved
+
type Algebra f a = f a -> a
+instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+
cata :: Functor f => Algebra f a -> Mu f -> a
+cata f = f . fmap (cata f) . outF
+
+
+
+
κατα-morphism: Finally length
+
All needed information for making length.
+
instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+length' :: Str -> Int
+length' = cata phi where
+ phi :: Algebra StrF Int -- StrF Int -> Int
+ phi (Cons a b) = 1 + b
+ phi Nil = 0
+
+main = do
+ l <- length' $ stringToStr "Toto"
+ ...
+
+
+
κατα-morphism: extension to Trees
+
Once you get the trick, it is easy to extent to most Functor.
+
type Tree = Mu TreeF
+data TreeF x = Node Int [x]
+
+instance Functor TreeF where
+ fmap f (Node e xs) = Node e (fmap f xs)
+
+depth = cata phi where
+ phi :: Algebra TreeF Int -- TreeF Int -> Int
+ phi (Node x sons) = 1 + foldr max 0 sons
+
+
+
Conclusion
+
Category Theory oriented Programming:
+
+
Focus on the type and operators
+
Extreme generalisation
+
Better modularity
+
Better control through properties of types
+
+
No cat were harmed in the making of this presentation.
Hakyll peut être vu comme un cms minimaliste. D’une façon plus générale, il s’agit d’une bibliothèque qui facilite la création automatique de fichiers.
+
D’un point de vue utilisateur voici comment j’écris mes articles :
+
+
J’ouvre un éditeur de texte (vim dans mon cas). J’édite un fichier markdow qui ressemble à ça :
J’ouvre mon navigateur et je rafraichis de temps en temps pour voir les changements.
+
Une fois satisfait, je lance un script minimal qui fait grosso modo un simple git push. Mon blog est hébergé sur github.
+
+
A ne pas y regarder de trop près, on peut réduire le rôle d’Hakyll à :
+
+
Créer (resp. mettre à jour) un fichier html lorsque je crée (resp. modifie) un fichier markdown.
+
+
Bien que cela semble facile, il y a de nombreux détails cachés :
+
+
Ajouter des métadatas comme des mots clés
+
Créer un page archive qui contient la liste de tous les articles
+
Gérer les fichier statiques
+
Créer un flux rss
+
Filtrer le contenu
+
Gérer les dépendances
+
+
Le travail d’Hakyll est de vous aider avec tout ça. Commençons par expliquer les concepts basiques.
+
Les concepts et la syntaxe
+
+
+
+
Pour chaque fichier que vous créer, il faut fournir :
+
+
un chemin de destination
+
une liste de filtres du contenu
+
+
Commençons par le cas le plus simple ; les fichiers statiques (images, fontes, etc…) Généralement, vous avec un répertoire source (ici le répertoire courant) et une répertoire destination _site.
Ce programme va copier static/foo.jpg dans _site/static/foo.jpg. C’est un peu lourd pour un simple cp. Maintenant comment faire pour transformer automatiquement un fichier markdown dans le bon html?
Mais horreur ! _site/posts/cthulhu.html n’est pas un html complet. Il ne possède ni header, ni footer, etc… C’est ici que nous utilisons des templates. J’ajoute une nouvelle directive dans le bloc “compile”.
La suite de l’article est en Anglais. Je la traduirai volontier si suffisamment de personnes me le demande gentillement.
+
Real customization
+
Now that we understand the basic functionality. How to:
+
+
use SASS?
+
add keywords?
+
simplify url?
+
create an archive page?
+
create an rss feed?
+
filter the content?
+
add abbreviations support?
+
manage two languages?
+
+
Use SASS
+
That’s easy. Simply call the executable using unixFilter. Of course you’ll have to install SASS (gem install sass). And we also use compressCss to gain some space.
Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in 2013-03-20-My-New-Post.md.
+
I migrated from an older blog and didn’t want to change my url. Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata published. And the solution is here:
And base.html is a standard template (simpler than post.html).
+
archiveCtx provide a context containing an html representation of a list of posts in the metadata named posts. It will be used in the templates/archive.html file with $posts$.
postList returns an html representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is post-item.html:
generate partially rendered posts (no css, js, etc…)
+
+
We could then render the posts twice. One for html rendering and another time for rss. Remark we need to generate the rss version to create the html one.
+
One of the great feature of Hakyll is to be able to save snapshots. Here is how:
Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed don’t need a source markdown file. Then we create a new file from no one. Instead of using match, we use create:
Great idea certainly steal from nanoc (my previous blog engine)!
+
Filter the content
+
As I just said, nanoc was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a lot of filters in nanoc. I lost some because I don’t use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type String -> String.
+
Also we generally want prefilters (to filter the markdown) and postfilters (to filter the html after the pandoc compilation).
It will search for all string starting by ‘%’ and it will search in the Map if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.
Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.
The full code is here. And except from the main file, I use literate Haskell. This way the code should be easier to understand.
+
If you want to know why I switched from nanoc:
+
My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.
+
So I had to do something about it. I had two choices:
+
+
Correct my old code (in Ruby)
+
Duplicate the core functionalities with Hakyll (in Haskell)
+
+
I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.
+
So far I am very happy with the switch. A complete build is about 4x faster. I didn’t broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.
+
I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:
Une extension de l'ensemble de Mandelbrot en 3D et en OpenGL
+
+
+
+
+
+
+
+
+
+
tlpl: Un exemple progressif d’utilisation d’Haskell. Vous pourrez voir un ensemble de Mandelbrot étendu à la troisième dimension. De plus le code sera très propre. Les détails de rendu sont séparés dans un module externe. Le code descriptif intéressant est concentré dans un environnement pur et fonctionnel. Vous pouvez vous inspirer de ce code utilisant le paradigme fonctional dans tous les languages.
This article goes further. It will show how to use functional programming with interactive programs. But more than that, it will show how to organize your code in a functional way. This article is more about functional paradigm than functional language. The code organization can be used in most imperative language.
+
As Haskell is designed for functional paradigm, it is easier to use in this context. In reality, the firsts sections will use an imperative paradigm. As you can use functional paradigm in imperative language, you can also use imperative paradigm in functional languages.
+
This article is about creating an useful and clean program. It can interact with the user in real time. It uses OpenGL, a library with imperative programming foundations. Despite this fact, most of the final code will remain in the pure part (no IO).
+
I believe the main audience for this article are:
+
+
Haskell programmer looking for an OpengGL tutorial.
+
People interested in program organization (programming language agnostic).
+
Fractal lovers and in particular 3D fractal.
+
People interested in user interaction in a functional paradigm.
+
+
I had in mind for some time now to make a Mandelbrot set explorer. I had already written a command line Mandelbrot set generator in Haskell. This utility is highly parallel; it uses the repa package1.
+
This time, we will not parallelize the computation. Instead, we will display the Mandelbrot set extended in 3D using OpenGL and Haskell. You will be able to move it using your keyboard. This object is a Mandelbrot set in the plan (z=0), and something nice to see in 3D.
+
Here are some screenshots of the result:
+
+
+
+
And you can see the intermediate steps to reach this goal:
+
+
+
+
From the 2nd section to the 4th it will be dirtier and dirtier. We start cleaning the code at the 5th section.
Also here, there is only one interesting line; the draw will occur in the function drawMandelbrot.
+
This function will provide a list of draw actions. Remember that OpenGL is imperative by design. Then, one of the consequence is you must write the actions in the right order. No easy parallel drawing here. Here is the function which will render something on the screen:
The mapM_ function is mainly the same as map but inside a monadic context. More precisely, this can be transformed as a list of actions where the order is important:
+
drawMandelbrot =
+ renderPrimitive Points $ do
+ color color1
+ vertex $ Vertex3 x1 y1 0
+ ...
+ color colorN
+ vertex $ Vertex3 xN yN 0
+
We also need some kind of global variables. In fact, global variable are a proof of a design problem. We will get rid of them later.
It uses the main Mandelbrot function for each complex \(c\). The Mandelbrot set is the set of complex number \(c\) such that the following sequence does not escape to infinity.
+
Let us define \(f_c: \)
+
fc(z) = z2 + c
+
The sequence is:
+
0 → fc(0) → fc(fc(0)) → ⋯ → fcn(0) → ⋯
+
Of course, instead of trying to test the real limit, we just make a test after a finite number of occurrences.
Well, if you download this file (look at the bottom of this section), compile it and run it this is the result:
+
+
+
+
A first very interesting property of this program is that the computation for all the points is done only once. It is a bit long before the first image appears, but if you resize the window, it updates instantaneously. This property is a direct consequence of purity. If you look closely, you see that allPoints is a pure list. Therefore, calling allPoints will always render the same result and Haskell is clever enough to use this property. While Haskell doesn’t garbage collect allPoints the result is reused for free. We did not specified this value should be saved for later use. It is saved for us.
+
See what occurs if we make the window bigger:
+
+
+
+
We see some black lines because we have drawn less point than there is on the surface. We can repair this by drawing little squares instead of just points. But, instead we will do something a bit different and unusual.
This time, instead of drawing all points, we will simply draw the edges of the Mandelbrot set. The method I use is a rough approximation. I consider the Mandelbrot set to be almost convex. The result will be good enough for the purpose of this tutorial.
+
We change slightly the drawMandelbrot function. We replace the Points by LineLoop
This function is interesting. For those not used to the list monad here is a natural language version of this function:
+
positivePoints =
+ for all x in the range [-width..width]
+ let y be smallest number s.t. mandel x y > 0
+ if y is on 0 then don't return a point
+ else return the value corresonding to (x,y,color for (x+iy))
+
In fact using the list monad you write like if you consider only one element at a time and the computation is done non deterministically. To find the smallest number such that mandel x y > 0 we use a simple dichotomy:
Now we will we extend to a third dimension. But, there is no 3D equivalent to complex. In fact, the only extension known are quaternions (in 4D). As I know almost nothing about quaternions, I will use some extended complex, instead of using a 3D projection of quaternions. I am pretty sure this construction is not useful for numbers. But it will be enough for us to create something that look nice.
+
This section is quite long, but don’t be afraid, most of the code is some OpenGL boilerplate. If you just want to skim this section, here is a high level representation:
+
+
+
OpenGL Boilerplate
+
+
set some IORef (understand variables) for states
+
+
Drawing:
+
+
set doubleBuffer, handle depth, window size…
+
Use state to apply some transformations
+
+
Keyboard: hitting some key change the state of IORef
+
+
Generate 3D Object
+
allPoints :: [ColoredPoint]
+allPoints =
+ for all (x,y), -width<x<width, 0<y<height
+ Let z be the minimal depth such that
+ mandel x y z > 0
+ add the points
+ (x, y, z,color)
+ (x,-y, z,color)
+ (x, y,-z,color)
+ (x,-y,-z,color)
+ + neighbors to make triangles
The most important part is the new multiplication instance. Modifying this formula will change radically the shape of the result. Here is the formula written in a more mathematical notation. I called the third component of these extended complex strange.
+
real((x, y, z) * (x′, y′, z′)) = xx′ − yy′ − zz′
+
im((x, y, z) * (x′, y′, z′)) = xy′ − yx′ + zz′
+
strange((x, y, z) * (x′, y′, z′)) = xz′ + zx′
+
Note how if z=z'=0 then the multiplication is the same to the complex one.
As we will use some 3D, we add some new directive in the boilerplate. But mainly, we simply state that will use some depth buffer. And also we will listen the keyboard.
We introduce some helper function to manipulate standard IORef. Mainly modVar x f is equivalent to the imperative x:=f(x), modFst (x,y) (+1) is equivalent to (x,y) := (x+1,y) and modSnd (x,y) (+1) is equivalent to (x,y) := (x,y+1)
And we use them to code the function handling keyboard. We will use the keys hjkl to rotate, oi to zoom and sedf to move. Also, hitting space will reset the view. Remember that angle and campos are pairs and zoom is a scalar. Also note (+0.5) is the function \x->x+0.5 and (-0.5) is the number -0.5 (yes I share your pain).
Not much to say about this function. Mainly there are two parts: apply some transformations, draw the object.
+
The 3D Mandelbrot
+
We have finished with the OpenGL section, let’s talk about how we generate the 3D points and colors. First, we will set the number of details to 200 pixels in the three dimensions.
This time, instead of just drawing some line or some group of points, we will show triangles. The function allPoints will provide a multiple of three points. Each three successive point representing the coordinate of each vertex of a triangle.
In fact, we will provide six ordered points. These points will be used to draw two triangles.
+
+
+
+
The next function is a bit long. Here is an approximative English version:
+
forall x from -width to width
+ forall y from -height to height
+ forall the neighbors of (x,y)
+ let z be the smalled depth such that (mandel x y z)>0
+ let c be the color given by mandel x y z
+ add the point corresponding to (x,y,z,c)
+
Also, I added a test to hide points too far from the border. In fact, this function show points close to the surface of the modified mandelbrot set. But not the mandelbrot set itself.
If you look at the function above, you see a lot of common patterns. Haskell is very efficient to make this better. Here is a harder to read but shorter and more generic rewritten function:
If you prefer the first version, then just imagine how hard it will be to change the enumeration of the point from (x,y) to (x,z) for example.
+
Also, we didn’t searched for negative values. This modified Mandelbrot is no more symmetric relatively to the plan y=0. But it is symmetric relatively to the plan z=0. Then I mirror these values.
The first approach to clean the code is to separate the GLUT/OpenGL part from the computation of the shape. Here is the cleaned version of the preceding section. Most boilerplate was put in external files.
This code is cleaner but many things doesn’t feel right. First, all the user interaction code is outside our main file. I feel it is okay to hide the detail for the rendering. But I would have preferred to control the user actions.
+
On the other hand, we continue to handle a lot rendering details. For example, we provide ordered vertices.
OpenGL and GLUT is done in C. In particular the mainLoop function is a direct link to the C library (FFI). This function is clearly far from the functional paradigm. Could we make this better? We will have two choices:
+
+
create our own mainLoop function to make it more functional.
+
deal with the imperative nature of the GLUT mainLoop function.
+
+As one of the goal of this article is to understand how to deal with existing libraries and particularly the one coming from imperative languages, we will continue to use the mainLoop function.
+
Our main problem come from user interaction. If you ask “the Internet”, about how to deal with user interaction with a functional paradigm, the main answer is to use functional reactive programming (FRP). I won’t use FRP in this article. Instead, I’ll use a simpler while less effective way to deal with user interaction. But The method I’ll use will be as pure and functional as possible.
+
+
Here is how I imagine things should go. First, what the main loop should look like if we could make our own:
+
functionalMainLoop =
+ Read user inputs and provide a list of actions
+ Apply all actions to the World
+ Display one frame
+ repetere aeternum
+
Clearly, ideally we should provide only three parameters to this main loop function:
+
+
an initial World state
+
a mapping between the user interactions and functions which modify the world
+
a function taking two parameters: time and world state and render a new world without user interaction.
+
+
Here is a real working code, I’ve hidden most display functions. The YGL, is a kind of framework to display 3D functions. But it can easily be extended to many kind of representation.
We first set the mapping between user input and actions. The type of each couple should be of the form (user input, f) where (in a first time) f:World -> World. It means, the user input will transform the world state.
The important part to glue our own type to the framework is to make our type an instance of the type class DisplayableWorld. We simply have to provide the definition of some functions.
The camera function will retrieve an object of type Camera which contains most necessary information to set our camera. The objects function will returns a list of objects. Their type is YObject. Note the generation of triangles is no more in this file. Until here we only used declarative pattern.
+
We also need to set all our transformation functions. These function are used to update the world state.
The resize is used to generate the 3D function. As I wanted the time spent to generate a more detailed view to grow linearly I use this not so straightforward formula.
Now the function which will generate points in 3D. The first parameter (res) is the resolution of the vertex generation. More precisely, res is distance between two points on one direction. We need it to “close” our shape.
+
The type Function3D is Point -> Point -> Maybe Point. Because we consider partial functions (for some (x,y) our function can be undefined).
Our code architecture feel very clean. All the meaningful code is in our main file and all display details are externalized. If you read the code of YGL.hs, you’ll see I didn’t made everything perfect. For example, I didn’t finished the code of the lights. But I believe it is a good first step and it will be easy to go further. Unfortunately the program of the preceding session is extremely slow. We compute the Mandelbulb for each frame now.
+
Before our program structure was:
+
Constant Function -> Constant List of Triangles -> Display
+
Now we have
+
Main loop -> World -> Function -> List of Objects -> Atoms -> Display
+
The World state could change. The compiler can no more optimize the computation for us. We have to manually explain when to redraw the shape.
+
To optimize we must do some things in a lower level. Mostly the program remains the same, but it will provide the list of atoms directly.
As we can use imperative style in a functional language, know you can use functional style in imperative languages. This article exposed a way to organize some code in a functional way. I’d like to stress the usage of Haskell made it very simple to achieve this.
+
Once you are used to pure functional style, it is hard not to see all advantages it offers.
+
The code in the two last sections is completely pure and functional. Furthermore I don’t use GLfloat, Color3 or any other OpenGL type. If I want to use another library in the future, I would be able to keep all the pure code and simply update the YGL module.
+
The YGL module can be seen as a “wrapper” around 3D display and user interaction. It is a clean separator between the imperative paradigm and functional paradigm.
+
If you want to go further, it shouldn’t be hard to add parallelism. This should be easy mainly because most of the visible code is pure. Such an optimization would have been harder by using directly the OpenGL library.
+
You should also want to make a more precise object. Because, the Mandelbulb is clearly not convex. But a precise rendering might be very long from O(n².log(n)) to O(n³).
+
+
+
+
Unfortunately, I couldn’t make this program to work on my Mac. More precisely, I couldn’t make the DevIL library work on Mac to output the image. Yes I have done a brew install libdevil. But even a minimal program who simply write some jpg didn’t worked. I tried both with Haskell and C.↩
+
Generally in Haskell you need to declare a lot of import lines. This is something I find annoying. In particular, it should be possible to create a special file, Import.hs which make all the necessary import for you, as you generally need them all. I understand why this is cleaner to force the programmer not to do so, but, each time I do a copy/paste, I feel something is wrong. I believe this concern can be generalized to the lack of namespace in Haskell.↩
+
I tried Complex Double, Complex Float, this current data type with Double and the actual version Float. For rendering a 1024x1024 Mandelbrot set it takes Complex Double about 6.8s, for Complex Float about 5.1s, for the actual version with Double and Float it takes about 1.6 sec. See these sources for testing yourself: https://gist.github.com/2945043. If you really want to things to go faster, use data Complex = C {-# UNPACK #-} !Float {-# UNPACK #-} !Float. It takes only one second instead of 1.6s.↩
tl;dr: Some hints on how to make great documentation for Haskell libraries.
+
+
Create a Tutorial module containing nothing except documentation.
+
Mention the Tutorial module in your cabal description
+
Use doctest to check your documentation is up to date
+
For more complex real world examples, link to the source of some test.
+
+
+
Great documentation make a big difference. A bad documentation could simply make people not using your lib.
+
My friend was learning Haskell. To start he tried a Haskell library to make a small application. The documentation was deprecated to the point he wasn’t able to make a basic example work. How do you believe he felt? What does he thought about Haskell in general?
+
So here are my hint on how to make a great documentation in Haskell.
+
Documentation can take many different form.
+
+
Tutorials/Guides – write some prose which friendly take a user by hand and help him
+
Examples – how to use each function
+
Generated API Documentation – haddock
+
+
Hints
+
Tutorials/Guides
+
+
Create a new module named Tutorial (or Guide.GuideTopic)
+
Create a link to the tutorial in the cabal description
To prevent obsolescence of your tutorial, use doctest.
+
That way when you’ll do a stack test or cabal test you’ll get errors if some example doesn’t work anymore.
+
Examples (doctest)
+
doctest is a great way to provide examples in your code documentation. These example will then be used as tests. Apparently it comes from Python community.
There are plenty of alternative solution. I provide the one I believe would be used by most people. So if you use github simply create an account on travis.
+
Add a .travis.yml file in your repo containing the content of the file here and remove the builds you don’t need. It will build your project using a lot of different GHC versions and environemnts.
+
If you are afraid by such its complexity you might just want to use this one:
If you didn’t declared your package to stackage, please do it. It isn’t much work. Just edit a file to add your package. And you’ll could be able to add another badge:
data MyData a b
+ = C1 a b -- ^ doc for constructor C1
+ | C2 a b -- ^ doc for constructor C2
+
+data MyData a b
+ = C { a :: TypeA -- ^ field a description
+ , b :: TypeB -- ^ field b description
+ }
+
Module:
+
{-|
+Module : MyModule
+Description: Short description
+Copyright : (c)
+License : MIT
+
+Here is a longer description of this module.
+With some code symbol @MyType@.
+And also a block of code:
+
+@
+data MyData = C Int Int
+
+myFunction :: MyData -> Int
+@
+
+-}
+
Documentation Structure:
+
module MyModule (
+ -- * Classes
+ C(..),
+ -- * Types
+ -- ** A data type
+ T,
+ -- ** A record
+ R,
+ -- * Some functions
+ f, g
+ ) where
+
That will generate headings.
+
Other Random Ideas
+
In Haskell we have great tools like hayoo! and hoogle.
+
And hackage and stackage provide also a lot of informations.
+
But generally we lack a lot of Tutorials and Guides. This post was an attempt to help people making more of them.
+
But there are other good ideas to help improve the situation.
+
Create a doc with link to best practices
+
In clojure when you create a new project using lein new my-project a directory doc is created for you. It contains a file with a link to this blog post:
If you try to search for some clojure function on a search engine there is a big chance the first result will link to:
+
+
clojuredocs.org: try to search for reduce, update-in or index for example
+
+
For each symbol necessiting a documentation. You don’t only have the details and standard documentation. You’ll also get:
+
+
Responsive Design (sometime you want to look at documentation on a mobile)
+
Contributed Examples
+
Contributed See Also section
+
Contributed notes/comments
+
+
clojuredocs.org is an independant website from the official Clojure website.
+
Most of the time, if you google the function you search you end up on clojuredocs for wich there are many contributions.
+
Currently stackage is closer to these feature than hackage. Because on stackage you have access to the README and also some comments by package.
+
I believe it would be more efficient to have at least a page by module and why not a page by symbol (data, functions, typeclasses…).
+
For example, we could provide details about foldl for example. Also as there would be less information to display, it will make the design cleaner.
+
Today, if you want to help documenting, you need to make a PR to the source of some library. While if we had an equivalent to clojuredocs for Haskell, adding documentation would simply be a few clicks away:
+
+
login
+
add/edit some example, comments, see-also section
+
+
There are more than 23k people on /r/haskell. If only 1% of them would take 10 minutes adding a bit of documentation it will certainly change a lot of things in the percieved documentation quality.
+
And last but not least,
+
Design is important
+
+
+
+
Design is a vague word. A good design should care not only about how something look, but also how users will interact with it. For example by removing things to focus on the essential.
+
When I stumble upon some random blog post or random specification in the Haskell community, I had too much a feeling of old fashioned design.
+
If you look at node.js community lot of their web page look cleaner, easier to read and in the end, more user friendly.
+
Haskell is very different from node, I wouldn’t like to replace all long and precise documentation with short human unprecise concepts. I don’t want to transform scientific papers by tweets.
+
But like the scientific community has upgraded with the use of LaTeX, I believe we could find something similar that would make, very clean environment for most of us. A kind of look and feel that will be
+
+
modern
+
device friendly (either on computer, mobile, tablet)
+
efficient, focus on what is most important and is helpful
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/00_preamble.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/00_preamble.lhs
new file mode 100644
index 0000000..ae51c26
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/00_preamble.lhs
@@ -0,0 +1,124 @@
+begindiv(intro)
+
+en: I really believe all developers should learn Haskell.
+en: I don't think everyone needs to be super Haskell ninjas,
+en: but they should at least discover what Haskell has to offer.
+en: Learning Haskell opens your mind.
+fr: Je pense vraiment que
+fr: tous les développeurs devraient apprendre Haskell.
+fr: Peut-être pas devenir des ninjas d'Haskell,
+fr: mais au moins savoir ce que ce langage a de particulier.
+fr: Son apprentissage ouvre énormément l'esprit.
+
+en: Mainstream languages share the same foundations:
+fr: La plupart des langages partagent les mêmes fondements :
+
+en: - variables
+en: - loops
+en: - pointers[^0001]
+en: - data structures, objects and classes (for most)
+fr: - les variables
+fr: - les boucles
+fr: - les pointeurs[^0001]
+fr: - les structures de données, les objets et les classes
+
+en: [^0001]: Even if most recent languages try to hide them, they are present.
+fr: [^0001]: Même si tous les langages récents essayent de les cacher, ils restent présents.
+
+en: Haskell is very different.
+en: The language uses a lot of concepts I had never heard about before.
+en: Many of those concepts will help you become a better programmer.
+fr: Haskell est très différent.
+fr: Ce langage utilise des concepts dont je n'avais jamais entendu parler avant.
+fr: Beaucoup de ces concepts pourront vous aider à devenir un meilleur développeur.
+
+en: But learning Haskell can be hard.
+en: It was for me.
+en: In this article I try to provide what I lacked during my learning.
+fr: Plier son esprit à Haskell peut être difficile.
+fr: Ce le fut pour moi.
+fr: Dans cet article, j'essaye de fournir les informations qui m'ont manquées lors de mon apprentissage.
+
+en: This article will certainly be hard to follow.
+en: This is on purpose.
+en: There is no shortcut to learning Haskell.
+en: It is hard and challenging.
+en: But I believe this is a good thing.
+en: It is because it is hard that Haskell is interesting.
+fr: Cet article sera certainement difficile à suivre.
+fr: Mais c'est voulu.
+fr: Il n'y a pas de raccourci pour apprendre Haskell.
+fr: C'est difficile.
+fr: Mais je pense que c'est une bonne chose.
+fr: C'est entre autres parce qu'Haskell est difficile qu'il est intéressant.
+
+en: The conventional method to learning Haskell is to read two books.
+en: First ["Learn You a Haskell"](http://learnyouahaskell.com) and just after ["Real World Haskell"](http://www.realworldhaskell.org).
+en: I also believe this is the right way to go.
+en: But to learn what Haskell is all about, you'll have to read them in detail.
+fr: La manière conventionnelle d'apprendre Haskell est de lire deux livres.
+fr: D'abord ["Learn You a Haskell"](http://haskell.fr/lyah/)
+fr: et ensuite ["Real World Haskell"](http://www.realworldhaskell.org).
+fr: Je pense aussi que c'est la bonne manière de s'y prendre.
+fr: Mais apprendre même un tout petit peu d'Haskell est presque impossible sans se plonger réellement dans ces livres.
+
+en: In contrast, this article is a very brief and dense overview of all major aspects of Haskell.
+en: I also added some information I lacked while I learned Haskell.
+fr: Cet article fait un résumé très dense et rapide des aspects majeurs d'Haskell.
+fr: J'y ai aussi rajouté des informations qui m'ont manqué pendant l'apprentissage de ce langage.
+
+fr: Pour les francophones : je suis désolé.
+fr: Je n'ai pas eu le courage de tout retraduire en français.
+fr: Sachez cependant que si vous êtes plusieurs à insister, je ferai certainement l'effort de traduire l'article en entier.
+fr: Et si vous vous sentez d'avoir une bonne âme je ne suis pas contre un peu d'aide.
+fr: Les sources de cet article sont sur [github](http://github.com/yogsototh/learn_haskell.git).
+
+en: The article contains five parts:
+fr: Cet article contient cinq parties :
+
+en: - Introduction: a short example to show Haskell can be friendly.
+en: - Basic Haskell: Haskell syntax, and some essential notions.
+en: - Hard Difficulty Part:
+en: - Functional style; a progressive example, from imperative to functional style
+en: - Types; types and a standard binary tree example
+en: - Infinite Structure; manipulate an infinite binary tree!
+en: - Hell Difficulty Part:
+en: - Deal with IO; A very minimal example
+en: - IO trick explained; the hidden detail I lacked to understand IO
+en: - Monads; incredible how we can generalize
+en: - Appendix:
+en: - More on infinite tree; a more math oriented discussion about infinite trees
+
+fr: - Introduction : un exemple rapide pour montrer qu'Haskell peut être facile.
+fr: - Les bases d'Haskell : La syntaxe et des notions essentielles
+fr: - Partie difficile :
+fr: - Style fonctionnel : un exemple progressif, du style impératif au style fonctionnel ;
+fr: - Types : la syntaxe et un exemple d'arbre binaire ;
+fr: - Structure infinie : manipulons un arbre infini !
+fr: - Partie de difficulté infernale :
+fr: - Utiliser les IO : un exemple très minimal ;
+fr: - Le truc des IO révélé : les détails cachés d'IO qui m'ont manqués
+fr: - Les monades : incroyable à quel point on peut généraliser
+fr: - Appendice :
+fr: - Revenons sur les arbres infinis : une discussion plus mathématique sur la manipulation d'arbres infinis.
+
+en: > Note: Each time you see a separator with a filename ending in `.lhs`
+en: > you can click the filename to get this file.
+en: > If you save the file as `filename.lhs`, you can run it with
+en: >
+en: > runhaskell filename.lhs
+en: >
+en: >
+en: > Some might not work, but most will.
+en: > You should see a link just below.
+
+fr: > Note: Chaque fois que vous voyez un séparateur avec un nom de fichier se terminant par `lhs`, vous pouvez cliquer sur le nom de fichier et télécharger le fichier.
+fr: > Si vous sauvegardez le fichier sour le nom `filename.lhs`, vous pouvez l'exécuter avec :
+fr: >
+fr: > runhaskell filename.lhs
+fr: >
+fr: >
+fr: > Certains ne marcheront pas, mais la majorité vous donneront un résultat.
+fr: > Vous devriez voir un lien juste en dessous.
+
+enddiv
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs
new file mode 100644
index 0000000..d1ddeed
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/00_hello_world.lhs
@@ -0,0 +1,76 @@
+
Introduction
+
+en:
Install
+fr:
Installation
+
+blogimage("Haskell-logo.png", "Haskell logo")
+
+en: There are different way to install Haskell, I would recommend to use
+fr: Aujourd'huil je considère que la manière la plus aisée d'installer Haskell est d'utiliser
+[`stack`](https://haskellstack.org).
+
+en: There are other way to install Haskell on your system you could visit,
+en: you can learn more about it by visiting
+fr: Il y a d'autres maniètres d'installer Haskell sur votre system,
+fr: vous pouvez en savoir plus en visitant
+[haskell.org](https://haskell.org)
+en: or
+fr: ou
+[haskell-lang.org](https://haskell-lang.org)
+
+en: Tools:
+fr: Outils:
+
+en: - `ghc`: Compiler similar to gcc for `C`.
+en: - `ghci`: Interactive Haskell (REPL)
+en: - `runhaskell`: Execute a program without compiling it. Convenient but very slow compared to compiled programs.
+fr: - `ghc`: Compilateur similaire à gcc pour le langage `C`.
+fr: - `ghci`: Console Haskell interactive (Read-Eval-Print Loop)
+fr: - `runhaskell`: Exécuter un programme sans le compiler. Pratique mais très lent comparé aux programmes compilés.
+
+en:
Don't be afraid
+fr:
Ne soyez pas effrayés!
+
+blogimage("munch_TheScream.jpg","The Scream")
+
+en: Many books/articles about Haskell start by introducing some esoteric formula (quick sort, Fibonacci, etc...).
+en: I will do the exact opposite.
+en: At first I won't show you any Haskell super power.
+en: I will start with similarities between Haskell and other programming languages.
+en: Let's jump to the mandatory "Hello World".
+fr: Beaucoup de livres/articles sur Haskell commencent par présenter des formules ésotériques (Algorithmes de tri rapide, suite de Fibonacci, etc...).
+fr: Je ferai l'exact opposé
+fr: En premier lieu je ne vous montrerai pas les super-pouvoirs d'Haskell.
+fr: Je vais commencer par les similarités avec les autres langages de programmation.
+fr: Commençons par l'indispensable "Hello World!".
+
+> main = putStrLn "Hello World!"
+
+en: To run it, you can save this code in a `hello.hs` and:
+fr: Pour l'exécuter, vous pouvez enregistrer ce code dans un fichier `hello.hs` et:
+
+
+~ runhaskell ./hello.hs
+Hello World!
+
+
+en: or if you use `stack` first run `stack setup` and then:
+fr: ou si vous utilisez `stack` lancez d'abord `stack setup` et ensuite :
+
+
+~ stack runhaskell ./hello.hs
+Hello World!
+
+
+
+en: You could also download the literate Haskell source.
+en: You should see a link just above the introduction title.
+en: Download this file as `00_hello_world.lhs` and:
+fr: Vous pouvez également télécharger la source Haskell littérale.
+fr: Vous devriez voir un lien juste au dessus du titre de l'introduction.
+fr: Téléchargez ce fichier en tant que `00_hello_world.lhs` et:
+
+
+~ runhaskell 00_hello_world.lhs
+Hello World!
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs
new file mode 100644
index 0000000..ec5ca13
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/10_hello_you.lhs
@@ -0,0 +1,52 @@
+en: Now, a program asking your name and replying "Hello" using the name you entered:
+fr: Maintenant, un programme qui demande votre nom et répond "Hello" suivit du nom que vous avez entré:
+
+> main = do
+> print "What is your name?"
+> name <- getLine
+> print ("Hello " ++ name ++ "!")
+
+en: First, let us compare this with similar programs in a few imperative languages:
+fr: Premièrement, comparons ce code avec ceux de quelques langages de programmation impératif:
+
+
+ # Python
+print "What is your name?"
+name = raw_input()
+print "Hello %s!" % name
+
+
+
+ # Ruby
+puts "What is your name?"
+name = gets.chomp
+puts "Hello #{name}!"
+
+
+
+// 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;
+}
+
+
+en: The structure is the same, but there are some syntax differences.
+en: The main part of this tutorial will be dedicated to explaining why.
+fr: La structure est la même, mais il y a quelques différences de syntaxe.
+fr: La partie principale de ce tutoriel sera consacrée à expliquer cela.
+
+en: In Haskell there is a `main` function and every object has a type.
+en: The type of `main` is `IO ()`.
+en: This means `main` will cause side effects.
+fr: En Haskell il y a une fonction `main` tous les objets ont un type.
+fr: Le type de `main` est `IO ()`.
+fr: Cela veut dire que `main` causera des effets secondaires.
+
+en: Just remember that Haskell can look a lot like mainstream imperative languages.
+fr: Rappelez-vous just que Haskell peut ressembler énormément aux principaux langages impératifs.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs
new file mode 100644
index 0000000..297e21b
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/20_very_basic.lhs
@@ -0,0 +1,159 @@
+en:
Very basic Haskell
+fr:
Les bases de Haskell
+
+blogimage("picasso_owl.jpg","Picasso minimal owl")
+
+en: Before continuing you need to be warned about some essential properties of Haskell.
+fr: Avant de continuer, vous devez êtres avertis à propos de propriétés essentielles de Haskell.
+
+en: _Functional_
+fr: _Fonctionnel_
+
+en: Haskell is a functional language.
+en: If you have an imperative language background, you'll have to learn a lot of new things.
+en: Hopefully many of these new concepts will help you to program even in imperative languages.
+fr: Haskell est un langage fonctionnel
+fr: Si vous avez déjà travaillé avec un langage impératif, vous devrez apprendre beaucoup de nouvelles choses.
+fr: Heureusement beaucoup de ces nouveaux concepts vous aidera à programmer même dans un langage impératif.
+
+en: _Smart Static Typing_
+fr: _Typage Statique Intelligent_
+
+en: Instead of being in your way like in `C`, `C++` or `Java`, the type system is here to help you.
+fr: Au lieu de bloquer votre chemin comme en `C`, `C++` ou `Java`, le système de typage est ici pour vous aider.
+
+en: _Purity_
+fr: _Pureté_
+
+en: Generally your functions won't modify anything in the outside world.
+en: 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.
+en: On the other hand, parallelism will be very easy to achieve.
+en: Haskell makes it clear where effects occur and where your code is pure.
+en: Also, it will be far easier to reason about your program.
+en: Most bugs will be prevented in the pure parts of your program.
+fr: Généralement vos fonctions ne modifieront rien du le monde extérieur.
+fr: Cela veut dire qu'elles ne peuvent pas modifier la valeur d'une variable,
+fr: lire du texte entré par un utilisateur,
+fr: écrire sur l'écran, lancer un missile.
+fr: D'un autre coté, avoir un code parallèle devient très facile.
+fr: Haskell rend très clair où les effets apparaissent et où le code est pur.
+fr: De plus, il devient beaucoup plus aisé de raisonner sur son programme.
+fr: La majorité des bugs seront évités dans les parties pures de votre programme.
+
+en: Furthermore, pure functions follow a fundamental law in Haskell:
+fr: En outre, les fonctions pures suivent une loi fondamentale en Haskell:
+
+en: > Applying a function with the same parameters always returns the same value.
+fr: > Appliquer une fonction avec les mêmes paramètres retourne toujours la même valeur.
+
+en: _Laziness_
+fr: _Paresse_
+
+en: Laziness by default is a very uncommon language design.
+en: By default, Haskell evaluates something only when it is needed.
+en: In consequence, it provides a very elegant way to manipulate infinite structures, for example.
+fr: La paresse par défaut est un choix de conception de langage très rare.
+fr: Par défaut, Haskell évalue quelque chose seulement lorsque cela est nécessaire.
+fr: En conséquence, cela fournit un moyen très élégant de manipuler des structures infinies, par exemple.
+
+en: A last warning about how you should read Haskell code.
+en: For me, it is like reading scientific papers.
+en: Some parts are very clear, but when you see a formula, just focus and read slower.
+en: Also, while learning Haskell, it _really_ doesn't matter much if you don't understand syntax details.
+en: If you meet a `>>=`, `<$>`, `<-` or any other weird symbol, just ignore them and follows the flow of the code.
+fr: Un dernier avertissement sur comment vous devriez lire le code Haskell.
+fr: Pour moi, c'est comme lire des papiers scientifiques.
+fr: Quelques parties sont très claires, mais quand vous voyez une formule, concentrez-vous dessus et lisez plus lentement.
+fr: De plus, lorsque vous apprenez Haskell, cela n'importe _vraiment_ pas si vous ne comprenez pas les détails syntaxiques.
+fr: Si vous voyez un `>>=`, `<$>`, `<-` ou n'importe quel symbole bizarre, ignorez-les et suivez le déroulement du code.
+
+en:
Function declaration
+fr:
Déclaration de fonctions
+
+en: You might be used to declaring functions like this:
+fr: Vous avez déjà dû déclarer des fonctions comme cela:
+
+en: In `C`:
+fr: En `C`:
+
+
+int f(int x, int y) {
+ return x*x + y*y;
+}
+
+
+en: In JavaScript:
+fr: En JavaScript:
+
+
+function f(x,y) {
+ return x*x + y*y;
+}
+
+
+en: in Python:
+fr: En Python:
+
+
+def f(x,y):
+ return x*x + y*y
+
+
+en: in Ruby:
+fr: En Ruby:
+
+
+def f(x,y)
+ x*x + y*y
+end
+
+
+en: In Scheme:
+fr: En Scheme:
+
+
+(define (f x y)
+ (+ (* x x) (* y y)))
+
+
+en: Finally, the Haskell way is:
+fr: Finalement, la manière de faire de Haskell est:
+
+
+f x y = x*x + y*y
+
+
+en: Very clean. No parenthesis, no `def`.
+fr: Très propre. Aucune parenthèse, aucun `def`.
+
+en: Don't forget, Haskell uses functions and types a lot.
+en: It is thus very easy to define them.
+en: The syntax was particularly well thought out for these objects.
+fr: N'oubliez pas, Haskell utilise beaucoup les fonctions et les types.
+fr: C'est très facile de les définir.
+fr: La syntaxe a été particulièrement réfléchie pour ces objets.
+
+en:
A Type Example
+fr:
Un exemple de type
+
+en: Although it is not mandatory, type information for functions is usually made
+en: explicit. It's not mandatory because the compiler is smart enough to discover
+en: it for you. It's a good idea because it indicates intent and understanding.
+fr: Même si ce n'est pas obligatoire, les informations de type pour les fonctions sont habituellement déclarées
+fr: explicitement. Ce n'est pas indispensable car le compilateur est suffisamment intelligent pour le déduire
+fr: à votre place. Cependant, c'est une bonne idée car cela montre bien l'intention du développeur et facilite la compréhension.
+
+en: Let's play a little.
+en: We declare the type using `::`
+fr: Jouons un peu.
+fr: On déclare le type en utilisant `::`
+
+> f :: Int -> Int -> Int
+> f x y = x*x + y*y
+>
+> main = print (f 2 3)
+
+~~~
+~ runhaskell 20_very_basic.lhs
+13
+~~~
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs
new file mode 100644
index 0000000..8d49e0c
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/21_very_basic.lhs
@@ -0,0 +1,23 @@
+en: Now try
+fr: Maintenant essayez
+
+> f :: Int -> Int -> Int
+> f x y = x*x + y*y
+>
+> main = print (f 2.3 4.2)
+
+en: You should get this error:
+fr: Vous devriez avoir cette erreur:
+
+~~~
+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)
+~~~
+
+en: The problem: `4.2` isn't an Int.
+fr: Le problème est que `4.2` n'est pas de type `Int` (_NDT: Il n'est pas un entier_)
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs
new file mode 100644
index 0000000..1ef0e2a
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/22_very_basic.lhs
@@ -0,0 +1,170 @@
+en: The solution: don't declare a type for `f` for the moment and let Haskell infer the most general type for us:
+fr: La soulution: ne déclarez pas de type pour `f` pour le moment et laissez Haskell inférer le type le plus général pour nous:
+
+> f x y = x*x + y*y
+>
+> main = print (f 2.3 4.2)
+
+en: It works!
+en: Luckily, we don't have to declare a new function for every single type.
+en: For example, in `C`, you'll have to declare a function for `int`, for `float`, for `long`, for `double`, etc...
+fr: Maintenant, ça marche!
+fr: Heureursement, nous n'avons pas à déclarer un nouvelle fonction pour chaque type différent.
+fr: Par exemple, en `C`, vous auriez dû déclarer un fonction pour `int`, pour `float`, pour `long`, pour `double`, etc...
+
+en: But, what type should we declare?
+en: To discover the type Haskell has found for us, just launch ghci:
+fr: Mais quel type devons nous déclarer?
+fr: Pour découvrir le type que Haskell a trouvé pour nous, lançons ghci:
+
+
+% 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
+
+
+en: Uh? What is this strange type?
+fr: Hein? Quel ce type étrange?
+
+~~~
+Num a => a -> a -> a
+~~~
+
+en: First, let's focus on the right part `a -> a -> a`.
+en: To understand it, just look at a list of progressive examples:
+fr: Premièrement, concentrons-nous sur la partie de droite: `a -> a -> a`.
+fr: Pour le comprendre, regardez cette liste d'exemples progressifs:
+
+en: --------------------------------------------------------------------------------------------------
+en: The written type Its meaning
+en: -------------------------- -----------------------------------------------------------------------
+en: `Int` the type `Int`
+en:
+en: `Int -> Int` the type function from `Int` to `Int`
+en:
+en: `Float -> Int` the type function from `Float` to `Int`
+en:
+en: `a -> Int` the type function from any type to `Int`
+en:
+en: `a -> a` the type function from any type `a` to the same type `a`
+en:
+en: `a -> a -> a` the type function of two arguments of any type `a` to the same type `a`
+en: --------------------------------------------------------------------------------------------------
+fr: --------------------------------------------------------------------------------------------------------------------------------------
+fr: Le type écrit Son sens
+fr: -------------------------- -----------------------------------------------------------------------------------------------------------
+fr: `Int` Le type `Int`
+fr:
+fr: `Int -> Int` Le type de la fonction qui prend un `Int` et retourne un `Int`
+fr:
+fr: `Float -> Int` Le type de la fonction qui prend un `Float` et retourne un `Int`
+fr:
+fr: `a -> Int` Le type de la fonction qui prend n'importe quel type de variable et retourne un `Int`
+fr:
+fr: `a -> a` Le type de la fonction qui prend n'importe quel type `a` et retourne une variable du même type `a`
+fr:
+fr: `a -> a -> a` Le type de la fonction qui prend de arguments de n'importe quel type`a` et retourne une variable de type `a`
+fr: --------------------------------------------------------------------------------------------------------------------------------------
+
+en: In the type `a -> a -> a`, the letter `a` is a _type variable_.
+en: It means `f` is a function with two arguments and both arguments and the result have the same type.
+en: The type variable `a` could take many different type values.
+en: For example `Int`, `Integer`, `Float`...
+fr: Dans le type `a -> a -> a`, la lettre `a` est une _variable de type_.
+fr: Cela signifie que `f` est une fonction avec deux arguments et que les deux arguments et le résultat ont le même type.
+fr: La variable de type `a` peut prendre de nombreuses valeurs différentes
+fr: Par exemple `Int`, `Integer`, `Float`...
+
+en: So instead of having a forced type like in `C` and having to declare a function
+en: for `int`, `long`, `float`, `double`, etc., we declare only one function like
+en: in a dynamically typed language.
+fr: Donc à la place d'avoir un type forcé comme en `C` et de devoir déclarer une fonction
+fr: pour `int`, `long`, `float`, `double`, etc., nous déclarons une seule fonction comme
+fr: dans un langage typé de façon dynamique.
+
+en: This is sometimes called parametric polymorphism. It's also called having your
+en: cake and eating it too.
+fr: C'est parfois appelé le polymorphisme paramétrique. C'est aussi appelé avoir un
+fr: gâteau et le manger.
+
+en: Generally `a` can be any type, for example a `String` or an `Int`, but also
+en: more complex types, like `Trees`, other functions, etc. But here our type is
+en: prefixed with `Num a => `.
+fr: Généralement `a` peut être de n'importe quel type, par exemple un `String` ou un `Int`, mais aussi
+fr: des types plus complexes comme `Trees`, d'autres fonctions, etc. Mais ici notre type est
+fr: préfixé par `Num a => `.
+
+en: `Num` is a _type class_.
+en: A type class can be understood as a set of types.
+en: `Num` contains only types which behave like numbers.
+en: More precisely, `Num` is class containing types which implement a specific list of functions, and in particular `(+)` and `(*)`.
+fr: `Num` est une _classe de type_.
+fr: Une classe de type peut être comprise comme un ensemble de types
+fr: `Num` contient seulement les types qui se comportent comme des nombres.
+fr: Plus précisement, `Num` est une classe qui contient des types qui implémentent une liste spécifique de fonctions,
+fr: en particulier `(+)` et `(*)`.
+
+en: Type classes are a very powerful language construct.
+en: We can do some incredibly powerful stuff with this.
+en: More on this later.
+fr: Les classes de types sont une structure de langage très puissante.
+fr: Nous pouvons faire des trucs incroyablement puissants avec.
+fr: Nous verrons cela plus tard.
+
+en: Finally, `Num a => a -> a -> a` means:
+fr: Finalement, `Num a => a -> a -> a` signifie:
+
+en: Let `a` be a type belonging to the `Num` type class.
+en: This is a function from type `a` to (`a -> a`).
+fr: soit `a` un type qui appartient à la classe `Num`.
+fr: C'est une fonction qui prend une variable de type `a` et retourne une fonction de type `(a -> a)`
+
+en: Yes, strange.
+en: In fact, in Haskell no function really has two arguments.
+en: Instead all functions have only one argument.
+en: 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.
+fr: Oui, c'est étrange.
+fr: En fait, en Haskell aucune fonction ne prend réellement deux arguments.
+fr: Au lieu de cela toutes les fonctions n'ont qu'un argument unique.
+fr: Mais nous retiendrons que prendre deux arguments est équivalent à n'en prendre qu'un et à retourner une fonction qui prend le second argument en paramètre.
+
+en: More precisely `f 3 4` is equivalent to `(f 3) 4`.
+en: Note `f 3` is a function:
+fr: Plus précisement `f 3 4` est équivalent à `(f 3) 4 `.
+fr: Remarque: `f 3` est une fonction:
+
+~~~
+f :: Num a => a -> a -> a
+
+g :: Num a => a -> a
+g = f 3
+
+g y ⇔ 3*3 + y*y
+~~~
+
+en: Another notation exists for functions.
+en: The lambda notation allows us to create functions without assigning them a name.
+en: We call them anonymous functions.
+en: We could also have written:
+fr: Une autre notation existe pour les fonctions.
+fr: La notation lambda nous autorise à créer des fonctions sans leur assigner un nom.
+fr: On les appelle des fonctions anonymes.
+fr: nous aurions donc pu écrire:
+
+~~~
+g = \y -> 3*3 + y*y
+~~~
+
+en: The `\` is used because it looks like `λ` and is ASCII.
+fr: Le `\` esst utilisé car il ressemble à un `λ` et est un caractère ASCII.
+
+en: If you are not used to functional programming your brain should be starting to heat up.
+en: It is time to make a real application.
+fr: Si vous n'êtes pas habitué à la programmation fonctionnelle, votre cerveau devrait commencer à chauffer
+fr: Il est temps de faire une vraie application.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs
new file mode 100644
index 0000000..f6881e3
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/23_very_basic.lhs
@@ -0,0 +1,12 @@
+en: But just before that, we should verify the type system works as expected:
+fr: Mais juste avant cela, nous devrions vérifier que le système de type marche comme nous le supposons:
+
+> f :: Num a => a -> a -> a
+> f x y = x*x + y*y
+>
+> main = print (f 3 2.4)
+
+en: It works, because, `3` is a valid representation both for Fractional numbers like Float and for Integer.
+en: As `2.4` is a Fractional number, `3` is then interpreted as being also a Fractional number.
+fr: Cela fonctionne, car `3` est une représentation valide autant pour les nombres fractionnaires comme Float que pour les entiers.
+fr: Comme `2.4` est un nombre fractionnaire, `3` est interprété comme une autre nombre fractionnaire
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs
new file mode 100644
index 0000000..8512c6b
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/10_Introduction/24_very_basic.lhs
@@ -0,0 +1,24 @@
+en: If we force our function to work with different types, it will fail:
+fr: Si nous forçons notre fonction à travailler avec des types différents, le test échouera:
+
+> 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)
+
+en: The compiler complains.
+en: The two parameters must have the same type.
+fr: Le compilateur se plaint.
+fr: Les deux paramètres doivent avoir le même type.
+
+en: If you believe that this is a bad idea, and that the compiler should make the transformation
+en: from one type to another for you, you should really watch this great (and funny) video:
+en: [WAT](https://www.destroyallsoftware.com/talks/wat)
+fr: Si vous pensez que c'est une mauvaise idée et que le compilateur devrait faire la transformation
+fr: depuis un type à un autre pour vous, vous devriez vraiment regarder cette vidéo géniale (et amusante):
+fr: [WAT](https://www.destroyallsoftware.com/talks/wat) (_NDT: En Anglais_)
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs
new file mode 100644
index 0000000..7744b8d
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/00_notations.lhs
@@ -0,0 +1,171 @@
+en:
Essential Haskell
+fr:
Notions essentielles
+
+blogimage("kandinsky_gugg.jpg","Kandinsky Gugg")
+
+en: I suggest that you skim this part.
+en: Think of it as a reference.
+en: Haskell has a lot of features.
+en: A lot of information is missing here.
+en: Come back here if the notation feels strange.
+fr: Je vous suggère de seulement survoler cette partie
+fr: Pensez-y seulement comme à une référence.
+fr: Haskell a beaucoup de caractèristiques
+fr: Il manque beaucoup d'informations ici.
+fr: Revenz ici si la notation vous semble étrange.
+
+en: I use the `⇔` symbol to state that two expression are equivalent.
+en: It is a meta notation, `⇔` does not exists in Haskell.
+en: I will also use `⇒` to show what the return value of an expression is.
+fr: J'utilise le symbole `⇔` pour signifier que deux expressions sont équivalentes.
+fr: C'est une notation extérieure, `⇔` n'existe pas en Haskell.
+fr: Je vais aussi utiliser le symoble `⇒` quelle est la valeur que retourne une fonction.
+
+
Notations
+
+en:
Arithmetic
+fr:
Arithmétique
+
+~~~
+3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3)
+~~~
+
+en:
Logic
+fr:
Logique
+
+~~~
+True || False ⇒ True
+True && False ⇒ False
+True == False ⇒ False
+en: True /= False ⇒ True (/=) is the operator for different
+fr: True /= False ⇒ True (/=) est l'opérateur pour "différent de"
+~~~
+
+en:
Powers
+fr:
Puissances
+
+~~~
+en: x^n for n an integral (understand Int or Integer)
+en: x**y for y any kind of number (Float for example)
+fr: x^n pour n un entier (comprenez Int ou Integer)
+fr: x**y pour y tout type de nombre (Float par exemple)
+~~~
+
+en: `Integer` has no limit except the capacity of your machine:
+fr: `Integer` n'a aucune limite à part la capacité de votre machine:
+
+~~~
+4^103
+102844034832575377634685573909834406561420991602098741459288064
+~~~
+
+Yeah!
+en: And also rational numbers FTW!
+en: But you need to import the module `Data.Ratio`:
+fr: Et aussi les nombres rationnels!
+fr: Mais vous avez besoin d'importer le module `Data.Ratio`
+
+~~~
+$ ghci
+....
+Prelude> :m Data.Ratio
+Data.Ratio> (11 % 15) * (5 % 3)
+11 % 9
+~~~
+
+en:
Lists
+fr:
Listes
+
+~~~
+en: [] ⇔ empty list
+en: [1,2,3] ⇔ List of integral
+en: ["foo","bar","baz"] ⇔ List of String
+en: 1:[2,3] ⇔ [1,2,3], (:) prepend one element
+en: 1:2:[] ⇔ [1,2]
+en: [1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concatenate
+en: [1,2,3] ++ ["foo"] ⇔ ERROR String ≠ Integral
+en: [1..4] ⇔ [1,2,3,4]
+en: [1,3..10] ⇔ [1,3,5,7,9]
+en: [2,3,5,7,11..100] ⇔ ERROR! I am not so smart!
+en: [10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1]
+fr: [] ⇔ liste vide
+fr: [1,2,3] ⇔ Liste d'entiers
+fr: ["foo","bar","baz"] ⇔ Liste de chaînes de caractères
+fr: 1:[2,3] ⇔ [1,2,3], (:) ajoute un élément au début
+fr: 1:2:[] ⇔ [1,2]
+fr: [1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concaténation de deux listes
+fr: [1,2,3] ++ ["foo"] ⇔ ERREUR String ≠ Integral
+fr: [1..4] ⇔ [1,2,3,4]
+fr: [1,3..10] ⇔ [1,3,5,7,9]
+fr: [2,3,5,7,11..100] ⇔ ERREUR! Je ne suis pas si intelligent!
+fr: [10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1]
+~~~
+
+en:
Strings
+fr:
Chaînes de caractères
+
+en: In Haskell strings are list of `Char`.
+fr: En Haskell les chaînes de caractères sont des listes de `Char`.
+
+~~~
+'a' :: Char
+"a" :: [Char]
+"" ⇔ []
+"ab" ⇔ ['a','b'] ⇔ 'a':"b" ⇔ 'a':['b'] ⇔ 'a':'b':[]
+"abc" ⇔ "ab"++"c"
+~~~
+
+en: > _Remark_:
+en: > In real code you shouldn't use list of char to represent text.
+en: > You should mostly use `Data.Text` instead.
+en: > If you want to represent a stream of ASCII char, you should use `Data.ByteString`.
+fr: > _Remarque_:
+fr: > Dans un vrai code vous n'utiliserez pas des listes de char pour représenter du texte.
+fr: > Vous utiliserez plus souvent `Data.Text` à la place.
+fr: > Si vous voulez représenter un chapelet de caractères ASCII, vous utiliserez `Data.ByteString`.
+
+
Tuples
+
+en: The type of couple is `(a,b)`.
+en: Elements in a tuple can have different types.
+fr: Le type d'un couple est `(a,b)`.
+fr: Les éléments d'un tuple peuvent avoir des types différents.
+
+~~~
+en: -- All these tuples are valid
+fr: -- tous ces tuples sont valides
+(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
+~~~
+
+en:
Deal with parentheses
+fr:
Traiter avec les parenthèses
+
+en: To remove some parentheses you can use two functions: `($)` and `(.)`.
+fr: Pour enlever des parenthèses vous pouvez utiliser deux fonctions: `($)` et `(.)`.
+
+~~~
+en: -- By default:
+fr: -- Par défaut:
+f g h x ⇔ (((f g) h) x)
+
+en: -- the $ replace parenthesis from the $
+en: -- to the end of the expression
+fr: -- le $ remplace les parenthèses depuis le $
+fr: -- jusqu'à la fin de l'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))
+
+en: -- (.) the composition function
+fr: -- (.) permet de faire des compositions de fonctions
+(f . g) x ⇔ f (g x)
+(f . g . h) x ⇔ f (g (h x))
+~~~
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs
new file mode 100644
index 0000000..5d543b2
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/01_basic/20_Essential_Haskell/10a_Functions.lhs
@@ -0,0 +1,99 @@
+en:
Useful notations for functions
+fr:
Notations utiles pour les fonctions
+
+en: Just a reminder:
+fr: Juste un mémo:
+
+~~~
+en: x :: Int ⇔ x is of type Int
+en: x :: a ⇔ x can be of any type
+en: x :: Num a => a ⇔ x can be any type a
+en: such that a belongs to Num type class
+en: f :: a -> b ⇔ f is a function from a to b
+en: f :: a -> b -> c ⇔ f is a function from a to (b→c)
+en: f :: (a -> b) -> c ⇔ f is a function from (a→b) to c
+fr: x :: Int ⇔ x est de type Int
+fr: x :: a ⇔ x peut être de n'importe quel type
+fr: x :: Num a => a ⇔ x peut être de n'importe quel type a
+fr: tant qu' a appartient à la classe de type Num
+fr: f :: a -> b ⇔ f est une fonction qui prend un a et retourne un b
+fr: f :: a -> b -> c ⇔ f est une fonction qui prend un a et retourne un (b→c)
+fr: f :: (a -> b) -> c ⇔ f est une fonction qui prend un (a→b) et retourne un c
+~~~
+
+en: Remember that defining the type of a function before its declaration isn't mandatory.
+en: Haskell infers the most general type for you.
+en: But it is considered a good practice to do so.
+fr: Rappelez-vous que définir le type d'une fonction avant sa déclaration n'est pas obligatoire.
+fr: Haskell infère le type le plus général pour vous.
+fr: Mais c'est considéré comme une bonne pratique.
+
+en: _Infix notation_
+fr: _Notation Infixée_
+
+> square :: Num a => a -> a
+> square x = x^2
+
+en: Note `^` uses infix notation.
+en: For each infix operator there its associated prefix notation.
+en: You just have to put it inside parenthesis.
+fr: Remarquez que `^` utilise une notation infixée.
+fr: Pour chaque opérateur infixe il y a une notation préfixée associée.
+fr: Vous devz juste l'écrire entre parenthèses.
+
+> square' x = (^) x 2
+>
+> square'' x = (^2) x
+
+en: We can remove `x` in the left and right side!
+en: It's called η-reduction.
+fr: Nous pouvons enlever le `x` dans les parties de gauche et de droite!
+fr: On appelle cela la η-réduction
+
+> square''' = (^2)
+
+en: Note we can declare functions with `'` in their name.
+en: Here:
+fr: Rmarquez qu nous pouvons déclarer des fonctions avec `'` dans leur nom.
+fr: Exemples:
+
+ > `square` ⇔ `square'` ⇔ `square''` ⇔ `square'''`
+
+_Tests_
+
+en: An implementation of the absolute function.
+fr: Une implémentation de la fonction absolue.
+
+> absolute :: (Ord a, Num a) => a -> a
+> absolute x = if x >= 0 then x else -x
+
+en: Note: the `if .. then .. else` Haskell notation is more like the
+en: `¤?¤:¤` C operator. You cannot forget the `else`.
+fr: Remarque: la notation de Haskell pour le `if .. then .. else` ressemble plus
+fr: à l'opérateur `¤?¤:¤` en C. Le `else` est obligatoire.
+
+en: Another equivalent version:
+fr: Une version équivalente:
+
+> absolute' x
+> | x >= 0 = x
+> | otherwise = -x
+
+en: > Notation warning: indentation is _important_ in Haskell.
+en: > Like in Python, bad indentation can break your code!
+fr: > Avertissement: l'indentation est _importante_ en Haskell.
+fr: > Comme en Python, une mauvaise indentation peut détruire votre code!
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs
new file mode 100644
index 0000000..02df0e4
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/10_Functions.lhs
@@ -0,0 +1,133 @@
+en:
Hard Part
+fr:
La Partie Difficile
+
+en: The hard part can now begin.
+fr: La partie difficile peut maintenant commencer.
+
+en:
Functional style
+fr:
Le style fonctionnel
+
+blogimage("hr_giger_biomechanicallandscape_500.jpg","Biomechanical Landscape by H.R. Giger")
+
+en: In this section, I will give a short example of the impressive refactoring ability provided by Haskell.
+en: We will select a problem and solve it in a standard imperative way.
+en: Then I will make the code evolve.
+en: The end result will be both more elegant and easier to adapt.
+fr: Dans cette section, je vais vous donner un court exemple de l'impressionante capacité de remaniement de Haskell.
+fr: Nous allons sélectionner un problème et le résoudre à la manière d'un langage impératif standard.
+fr: Ensuite, je ferais évoluer le code.
+fr: Le résultat final sera plus élégant et plus facile à adapter.
+
+en: Let's solve the following problem:
+fr: résolvons les problèmes suivants:
+
+en: > Given a list of integers, return the sum of the even numbers in the list.
+fr: > Soit une liste d'entiers, retourner la somme des nombres pairs de cette liste.
+ >
+en: > example:
+fr: > exemple:
+ > `[1,2,3,4,5] ⇒ 2 + 4 ⇒ 6`
+
+en: To show differences between functional and imperative approaches,
+en: I'll start by providing an imperative solution (in JavaScript):
+fr: Pour montrer les différences entre les approches fonctionnelle et impérative,
+fr: je vais commencer par donner la solution impérative (en JavaScript):
+
+
+function evenSum(list) {
+ var result = 0;
+ for (var i=0; i< list.length ; i++) {
+ if (list[i] % 2 ==0) {
+ result += list[i];
+ }
+ }
+ return result;
+}
+
+
+en: In Haskell, by contrast, we don't have variables or a for loop.
+en: One solution to achieve the same result without loops is to use recursion.
+fr: En Haskell, en revanche, nous n'avons pas de variables ou un boucle `for`.
+fr: Une des solutions pour parvenir au même résultat sans boucles est d'utiliser la récursion.
+
+en: > _Remark_:
+en: > Recursion is generally perceived as slow in imperative languages.
+en: > But this is generally not the case in functional programming.
+en: > Most of the time Haskell will handle recursive functions efficiently.
+fr: > _Remarque_:
+fr: > La récursion est souvent perçue comme lente dans les langages impératifs.
+fr: > Mais ce n'est généralement pas le cas en programmation fonctionnelle.
+fr: > La plupart du temps Haskell gérera les fonctions récursives efficacement.
+
+en: Here is a `C` version of the recursive function.
+en: Note that for simplicity I assume the int list ends with the first `0` value.
+fr: Voici la version `C` de la fonction récursive.
+fr: Remarquez que je suppose que la liste d'int fini avec la première valeur `0`.
+
+
+
+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);
+ }
+ }
+}
+
+
+en: Keep this code in mind. We will translate it into Haskell.
+en: First, however, I need to introduce three simple but useful functions we will use:
+fr: Gardez ce code à l'esprit. Nous allons le traduire en Haskell.
+fr: Premièrement,
+
+
+even :: Integral a => a -> Bool
+head :: [a] -> a
+tail :: [a] -> [a]
+
+
+en: `even` verifies if a number is even.
+fr: `even` vérifie si un nombre est pair.
+
+
+even :: Integral a => a -> Bool
+even 3 ⇒ False
+even 2 ⇒ True
+
+
+en: `head` returns the first element of a list:
+fr: `head` retourne le premier élément d'une liste:
+
+
+head :: [a] -> a
+head [1,2,3] ⇒ 1
+head [] ⇒ ERROR
+
+
+en: `tail` returns all elements of a list, except the first:
+fr: `tail` retourne tous les éléments d'une liste, sauf le premier:
+
+
+tail :: [a] -> [a]
+tail [1,2,3] ⇒ [2,3]
+tail [3] ⇒ []
+en: tail [] ⇒ ERROR
+fr: tail [] ⇒ ERREUR
+
+
+en: Note that for any non empty list `l`,
+en: `l ⇔ (head l):(tail l)`
+fr: Remarquez que pour toute liste non-vide `l`,
+fr: `l ⇔ (head l):(tail l)`
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs
new file mode 100644
index 0000000..cc34f13
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/11_Functions.lhs
@@ -0,0 +1,80 @@
+en: The first Haskell solution.
+en: The function `evenSum` returns the sum of all even numbers in a list:
+fr: La première solution en Haskell.
+fr: La fonction `evenSum` retourne la somme de tous les nombres pairs d'une liste:
+
+> -- 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
+
+en: To test a function you can use `ghci`:
+fr: Pour tester une fonction nous pouvons utiliser `ghci`:
+
+
+% 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
+
+
+en: Here is an example of execution[^2]:
+fr: Voici un exemple d'exécution[^2]:
+
+en: [^2]: I know I'm cheating. But I will talk about non-strictness later.
+fr: [^2]: Je sais que je triche. Mais je parlerais de la non-rigueur plus tard.
+
+
+*Main> evenSum [1..5]
+accumSum 0 [1,2,3,4,5]
+en: 1 is odd
+fr: 1 est impair
+accumSum 0 [2,3,4,5]
+en: 2 is even
+fr: 2 est pair
+accumSum (0+2) [3,4,5]
+en: 3 is odd
+fr: 3 est impair
+accumSum (0+2) [4,5]
+en: 2 is even
+fr: 4 est pair
+accumSum (0+2+4) [5]
+en: 5 is odd
+fr: 5 est impair
+accumSum (0+2+4) []
+l == []
+0+2+4
+0+6
+6
+
+
+en: Coming from an imperative language all should seem right.
+en: In fact, many things can be improved here.
+en: First, we can generalize the type.
+fr: En venant d'un langage impératif, tout devrait vous sembler juste.
+fr: En fait, beaucoup de choses peuvent être améliorées ici.
+fr: Tout d'abord, nous pouvons généraliser le type.
+
+
+evenSum :: Integral a => [a] -> a
+
+
+
+
+> main = do print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs
new file mode 100644
index 0000000..8907b1e
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/12_Functions.lhs
@@ -0,0 +1,23 @@
+en: Next, we can use sub functions using `where` or `let`.
+fr: Ensuite, nous pouvons utiliser des sous-fonctions grâce à `where` et `let`.
+en: This way our `accumSum` function won't pollute the namespace of our module.
+fr: Ansi, notre fonction `accumSum` ne polluera pas le _namespace_ de notre module
+
+> -- 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
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs
new file mode 100644
index 0000000..a2849c4
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/13_Functions.lhs
@@ -0,0 +1,64 @@
+en: Next, we can use pattern matching.
+fr: Puis on utilise le _pattern matching_
+
+> -- 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
+
+en: What is pattern matching?
+en: Use values instead of general parameter names[^021301].
+fr: Qu'est ce que le _pattern matching_ ?
+fr: Il s'agit d'utiliser des valeurs au lieu de noms de paramètres généraux.
+
+en: [^021301]: For the brave, a more complete explanation of pattern matching can be found [here](http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html).
+fr: [^021301]: Pour les plus courageux, une explication plus complète du _pattern matching_ peut être trouvée [ici](http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html) (_NdT: En anglais_)
+
+en: Instead of saying: `foo l = if l == [] then else `
+en: You simply state:
+fr: Au lieu d'écrire: `foo l = if l == [] then else `
+fr: Vous écrivez tout simplement :
+
+
+foo [] =
+foo l =
+
+
+en: But pattern matching goes even further.
+en: It is also able to inspect the inner data of a complex value.
+en: We can replace
+fr: Mais le _pattern matching_ peut aller encore plus loin.
+fr: Il est également capable d'inspect les données internes d'un valeur complexe.
+fr: Nous pouvons ainsi remplacer
+
+
+foo l = let x = head l
+ xs = tail l
+ in if even x
+ then foo (n+x) xs
+ else foo n xs
+
+
+en: with
+fr: par
+
+
+foo (x:xs) = if even x
+ then foo (n+x) xs
+ else foo n xs
+
+
+en: This is a very useful feature.
+en: It makes our code both terser and easier to read.
+fr: C'est une caractéristique très utile.
+fr: Notre code est ainsi plus concis et plus facile à lire.
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs
new file mode 100644
index 0000000..9a92064
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/14_Functions.lhs
@@ -0,0 +1,37 @@
+en: In Haskell you can simplify function definitions by η-reducing them.
+en: For example, instead of writing:
+fr: Avec Haskell, nous pouvons simplifier les défitions des fonctions en les _η-réduisant_ .
+fr: Par exemple, au lieu d'écrire:
+
+
+en: f x = (some expresion) x
+fr: f x = (expression) x
+
+
+en: you can simply write
+fr: Nous pouvons écrire
+
+
+en: f = some expression
+fr: f = expression
+
+
+en: We use this method to remove the `l`:
+fr: Utilisons cette méthode pour retirer le `l`:
+
+> -- 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
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs
new file mode 100644
index 0000000..6007a34
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/15_Functions.lhs
@@ -0,0 +1,123 @@
+en:
Higher Order Functions
+fr:
Fonctions d'ordre supérieur
+
+blogimage("escher_polygon.png","Escher")
+
+en: To make things even better we should use higher order functions.
+en: What are these beasts?
+en: Higher order functions are functions taking functions as parameters.
+fr: Pour rendre les choses plus faciles, nous devrions utiliser des fonctions d'ordre supérieur.
+fr: Ce sont des fonctions qui prennent des fonctions en paramètres
+
+en: Here are some examples:
+fr: Voici quelques exemples:
+
+
+filter :: (a -> Bool) -> [a] -> [a]
+map :: (a -> b) -> [a] -> [b]
+foldl :: (a -> b -> a) -> a -> [b] -> a
+
+
+en: Let's proceed by small steps.
+fr: Procédons par étapes.
+
+
+-- Version 5
+evenSum l = mysum 0 (filter even l)
+ where
+ mysum n [] = n
+ mysum n (x:xs) = mysum (n+x) xs
+
+
+en: where
+fr: où
+
+
+filter even [1..10] ⇔ [2,4,6,8,10]
+
+
+en: The function `filter` takes a function of type (`a -> Bool`) and a list of type `[a]`.
+en: It returns a list containing only elements for which the function returned `true`.
+fr: La fonction `filter` prend une fonction du type (`a -> Bool`) et une liste de type `[a]`.
+fr: Elle retourne une liste qui contient seulement les élements pour qui la fonction a retourné `True`.
+
+en: Our next step is to use another technique to accomplish the same thing as a loop.
+en: We will use the `foldl` function to accumulate a value as we pass through the list.
+en: The function `foldl` captures a general coding pattern:
+fr: La prochaine étape est d'utiliser une autre technique pour accomplir la même chose qu'une boucle.
+fr: Nous allons utiliser la fonction `foldl` pour accumuler une valeur au fur et à mesure que l'on parcoure la liste.
+fr: La fonction `foldl` capture un modèle de code général:
+
+
+
+en: Which can be replaced by:
+fr: Qui peut être remplacé par:
+
+
+myfunc list = foldl barinitialValuelist
+
+
+en: If you really want to know how the magic works, here is the definition of `foldl`:
+fr: Si vous souhaitez vraiment savoir comment la magie se produit, voici la définition de `foldl`:
+
+
+foldl f z [] = z
+foldl f z (x:xs) = foldl f (f z x) xs
+
+
+
+foldl f z [x1,...xn]
+⇔ f (... (f (f z x1) x2) ...) xn
+
+
+en: But as Haskell is lazy, it doesn't evaluate `(f z x)` and simply pushes it onto the stack.
+en: This is why we generally use `foldl'` instead of `foldl`;
+en: `foldl'` is a _strict_ version of `foldl`.
+en: If you don't understand what lazy and strict means,
+en: don't worry, just follow the code as if `foldl` and `foldl'` were identical.
+fr: Mais comme Haskell est paresseux, il n'évalue pas `(f z x)` et le met simplement dans la pile.
+fr: C'est pourquoi on utilise généralement `foldl'`, une version _stricte_ de `foldl`,
+fr: Si vous ne comprenez pas encore ce que _paresseux_ ou _strict_ signifie,
+fr: ne vous inquiétez pas, suivez le code comme si `foldl'` et `foldl` étaient identiques
+
+en: Now our new version of `evenSum` becomes:
+fr: Maintenant notre version de `evenSum` devient:
+
+
+-- Version 6
+en: -- foldl' isn't accessible by default
+en: -- we need to import it from the module Data.List
+fr: -- foldl' n'est pas accessible par défaut
+fr: -- nous devons l'importer depuis le module Data.List
+import Data.List
+evenSum l = foldl' mysum 0 (filter even l)
+ where mysum acc value = acc + value
+
+
+en: We can also simplify this by using directly a lambda notation.
+en: This way we don't have to create the temporary name `mysum`.
+fr: Nous pouvons aussi simplifier cela en utilisant une _lambda-notation_.
+fr: Ainsi nous n'avons pas besoin de créer le nom temporaire `mySum`.
+
+> -- 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)
+
+en: And of course, we note that
+fr: Et bien sûr, nous remarquons que
+
+
+(\x y -> x+y) ⇔ (+)
+
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs
new file mode 100644
index 0000000..fc1c1c1
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/16_Functions.lhs
@@ -0,0 +1,148 @@
+en: Finally
+fr: Finalement
+
+
+-- Version 8
+import Data.List (foldl')
+evenSum :: Integral a => [a] -> a
+evenSum l = foldl' (+) 0 (filter even l)
+
+
+en: `foldl'` isn't the easiest function to grasp.
+en: If you are not used to it, you should study it a bit.
+fr: `foldl'` n'est pas la fonction la plus facile à prendre en main.
+fr: Si vous n'y êtes pas habitué, vous devriez l'étudier un peu.
+
+en: To help you understand what's going on here, let's look at a step by step evaluation:
+fr: Pour mieux comprendre ce qui se passe ici, étudions une évaluation étape par étape:
+
+
+
+
+en: Another useful higher order function is `(.)`.
+en: The `(.)` function corresponds to mathematical composition.
+fr: Une autre fonction d'ordre supérieur utile est `(.)`.
+fr: Elle correspond à une composition en mathématiques.
+
+
+(f . g . h) x ⇔ f ( g (h x))
+
+
+en: We can take advantage of this operator to η-reduce our function:
+fr: Nous pouvons profiter de cet opérateur pour η-réduire notre fonction:
+
+
+-- Version 9
+import Data.List (foldl')
+evenSum :: Integral a => [a] -> a
+evenSum = (foldl' (+) 0) . (filter even)
+
+
+en: Also, we could rename some parts to make it clearer:
+fr: Nous pouvons maintenant renommer certaines parties pour rendre le tout plus clair:
+
+> -- Version 10
+> import Data.List (foldl')
+> sum' :: (Num a) => [a] -> a
+> sum' = foldl' (+) 0
+> evenSum :: Integral a => [a] -> a
+> evenSum = sum' . (filter even)
+>
+
+en: It is time to discuss the direction our code has moved as we introduced more functional idioms.
+en: What did we gain by using higher order functions?
+fr: Il est temps de discuter de la direction qu'a pris notre code depuis que nous avons introduit plus d'idiomes fonctionnels.
+fr: Que gagnons-nous à utiliser des fonctions d'ordre supérieur?
+
+en: At first, you might think the main difference is terseness. But in fact, it has
+en: more to do with better thinking. Suppose we want to modify our function
+en: slightly, for example, to get the sum of all even squares of elements of the list.
+fr: D'abord, vous pourriez penser que la principale différence est la brièveté. Mais en réalité,
+fr: il s'agit d'une meilleure façon de penser. Supposons que nous voulons modifier légèrement notre fonction,
+fr: par exemple, pour qu'elle renvoie la somme de tous les carrés pairs des éléments de la liste.
+
+~~~
+[1,2,3,4] ▷ [1,4,9,16] ▷ [4,16] ▷ 20
+~~~
+
+en: Updating version 10 is extremely easy:
+fr: Mettre la version 10 à jour est très facile:
+
+> squareEvenSum = sum' . (filter even) . (map (^2))
+> squareEvenSum' = evenSum . (map (^2))
+
+en: We just had to add another "transformation function"[^0216].
+fr: Nous avons juste eu à ajouter une autre "fonction de trabsformation"[^0216].
+
+~~~
+map (^2) [1,2,3,4] ⇔ [1,4,9,16]
+~~~
+
+en: The `map` function simply applies a function to all the elements of a list.
+fr: La fonction `map` applique simplementune fonction à tous les élements d'une liste.
+
+en: We didn't have to modify anything _inside_ the function definition.
+en: This makes the code more modular.
+en: But in addition you can think more mathematically about your function.
+en: You can also use your function interchangably with others, as needed.
+en: That is, you can compose, map, fold, filter using your new function.
+fr: Nous n'avons rien modifié _à l'intérieur_ de notre définition de fonction.
+fr: Cela rend le code plus modulaire.
+fr: En plus de cela, vous pouvez penser à votre fonction plus mathématiquement.
+fr: Vous pouvez aussi utilier votre fonction avec d'autres, au besoin:
+fr: vous pouvez utiliser `compose`, `map`, `fold` ou `filter` sur notre nouvelle fonction.
+
+en: Modifying version 1 is left as an exercise to the reader ☺.
+fr: Modifier la version 1 est laissé comme un exercice pour le lecteur ☺.
+
+en: If you believe we have reached the end of generalization, then know you are very wrong.
+en: For example, there is a way to not only use this function on lists but on any recursive type.
+en: If you want to know how, I suggest you to read this quite fun article: [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).
+fr: Si vous croyez avoir atteint le bout de la généralisation, vous avez tout faux.
+fr: Par example, il y a un moyen d'utiliser cette fonction non seulement sur les listes mais aussi sur n'importe quel type récursif.
+fr: Si vous voulez savoir comment, je vous suggère de lire cet article: [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) (_NDT: en anglais, mais là vous vous en seriez douté je pense ☺_)
+
+en: This example should show you how great pure functional programming is.
+en: Unfortunately, using pure functional programming isn't well suited to all usages.
+en: Or at least such a language hasn't been found yet.
+fr: Cet exemple montre à quel point la programmation fonctionnelle pure est géniale.
+fr: Malheureusement, utiliser cet outil n'est pas adapté à tous les besoins.
+fr: Ou alors un langage qui le premettrait n'a pas encore été trouvé.
+
+en: One of the great powers of Haskell is the ability to create DSLs
+en: (Domain Specific Language)
+en: making it easy to change the programming paradigm.
+fr: Une des grands pouvoirs de Haskell est sa capacité à créer des DSLs
+fr: (_Domain Specific Language_, en français : _langage spécifique à un domaine_)
+fr: Il est ainsi facile de changer le pardigme de programmation
+
+en: In fact, Haskell is also great when you want to write imperative style
+en: programming. Understanding this was really hard for me to grasp when first
+en: learning Haskell. A lot of effort tends to go into explaining the superiority
+en: of the functional approach. Then when you start using an imperative style with
+en: Haskell, it can be hard to understand when and how to use it.
+fr: En fait, Haskell peut très bien vous permettre d'écrire des programmes impératifs.
+fr: Comprendre cela a été très difficile pour moi lorsque j'apprenais Haskell.
+fr: Beaucoup d'efforts tendent à expliquer la supériorité de l'approche fonctionnele.
+fr: Puis lorsque vous commencez à utliser le style impératif en Haskell,
+fr: Il peut être difficile de comprendre quand et où l'utliser.
+
+en: But before talking about this Haskell super-power, we must talk about another
+en: essential aspect of Haskell: _Types_.
+fr: Mais avant de parler de ce super-pouvoir de Haskell, nous devons parler
+fr: d'un autre aspet essentiel: les _Types_.
+
+
+
+> main = print $ evenSum [1..10]
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs
new file mode 100644
index 0000000..175db29
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/20_Types.lhs
@@ -0,0 +1,137 @@
+en:
Types
+fr:
Les types
+
+blogimage("salvador-dali-the-madonna-of-port-lligat.jpg","Dali, the madonna of port Lligat")
+
+ > %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.
+
+en: In Haskell, types are strong and static.
+fr: En Haskell, les types sont forts et statiques.
+
+en: Why is this important? It will help you _greatly_ to avoid mistakes.
+en: In Haskell, most bugs are caught during the compilation of your program.
+en: And the main reason is because of the type inference during compilation.
+en: Type inference makes it easy to detect where you used the wrong parameter at the wrong place, for example.
+fr: Pourquoi est-ce important? Cela vous aidera a éviter _beaucoup_ d'erreurs.
+fr: En Haskell, la majorité des bugs est repérée durant la compilation de votre programme.
+fr: Et la raison principale de cela est l'inférence de type durant la compilation.
+fr: L'inférence de type permet de détecter plus facilement lorsque vous utilisez le mauvais paramètre au mauvais endroit, par exemple
+
+en:
Type inference
+fr:
Inférence de type
+
+en: Static typing is generally essential for fast execution.
+en: But most statically typed languages are bad at generalizing concepts.
+en: Haskell's saving grace is that it can _infer_ types.
+fr: Le typage statique est généralement essentiel pour une exécution rapide.
+fr: Mais la plupart des langages typés statiquement ont du mal à généraliser des concepts.
+fr: La "grâce salvatrice" de Haskell est qu'il peut _inférer_ des types.
+
+en: Here is a simple example, the `square` function in Haskell:
+fr: Voici un exemple simple, la fonction `square` en Haskell:
+
+
+square x = x * x
+
+
+en: This function can `square` any Numeral type.
+en: You can provide `square` with an `Int`, an `Integer`, a `Float` a `Fractional` and even `Complex`. Proof by example:
+fr: Cette fonction peut mettre au carré n'importe quel type `Numeral`.
+fr: Vous pouvez l'utilser avec un `Int`, un `Integer`, un `Float`, un `Fractional` ou même un `Complex`. Preuve par l'exemple:
+
+~~~
+% ghci
+GHCi, version 7.0.4:
+...
+Prelude> let square x = x*x
+Prelude> square 2
+4
+Prelude> square 2.1
+4.41
+en: Prelude> -- load the Data.Complex module
+fr: Prelude> -- charge le module Data.Complex
+Prelude> :m Data.Complex
+Prelude Data.Complex> square (2 :+ 1)
+3.0 :+ 4.0
+~~~
+
+en: `x :+ y` is the notation for the complex (x + iy).
+fr: `x :+ y` est la notation pour le complexe (x + iy)
+
+en: Now compare with the amount of code necessary in C:
+fr: Comparons maintenant avec la quantité de code nécessaire pour le faire en 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);
+
+
+en: For each type, you need to write a new function.
+en: The only way to work around this problem is to use some meta-programming trick, for example using the pre-processor.
+en: In C++ there is a better way, C++ templates:
+fr: Pour chaque type, vous avez besoin d'écrire une nouvelle fonction.
+fr: Le seul moyen de se débarrasser de ce problème est d'utiliser des astuces de méta-programmation, par exemple en utilisant le pré-processeur.
+fr: en C++ il y a un meilleur moyen, les _templates_:
+
+
+#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;
+}
+
+
+en: C++ does a far better job than C in this regard.
+en: But for more complex functions the syntax can be hard to follow:
+en: see [this article](http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/) for example.
+fr: C++ fait un bien meilleur travail que C ici.
+fr: Mais pour des fonctions plus complexes, la syntaxe sera difficile à suivre.
+fr: Voyez [cet article](http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/) pour quelques exemples. (_NDT: toujours en anglais)
+
+en: In C++ you must declare that a function can work with different types.
+en: In Haskell, the opposite is the case.
+en: The function will be as general as possible by default.
+fr: En C++ vous devez déclarer qu'une fonction peut marcher avec différents types.
+fr: En Haskell, c'est le contraire.
+fr: La fonction sera aussi générale que possible par défaut.
+
+en: Type inference gives Haskell the feeling of freedom that dynamically typed languages provide.
+en: But unlike dynamically typed languages, most errors are caught before run time.
+en: Generally, in Haskell:
+fr: L'inférence de type donne à Haskell le sentiment de liberté que les langages dynamiquement typés proposent.
+fr: Mais contrairement aux langages dynamiquement typés, la majorité des erreurs est détectée avant de lancer le programme.
+fr: Généralement, en Haskell:
+
+en: > "if it compiles it certainly does what you intended"
+fr: > "Si ça compile, ça fait certainement ce que vous attendiez."
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs
new file mode 100644
index 0000000..8bfb530
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/21_Types.lhs
@@ -0,0 +1,19 @@
+en:
Type construction
+fr:
Construction de types
+
+en: You can construct your own types.
+en: First, you can use aliases or type synonyms.
+fr: Vous pouvez construire vos propres types.
+fr: D'abord, vous pouvez utiliser des alias ou des synonymes de types.
+
+> 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
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs
new file mode 100644
index 0000000..e41679b
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/22_Types.lhs
@@ -0,0 +1,90 @@
+
+en: But it doesn't protect you much.
+en: Try to swap the two parameter of `showInfos` and run the program:
+fr: Mais cela ne vous protège pas tellement.
+fr: Essayez d'inverser les deux paramètres de `showInfos` et lancez le programme:
+
+
+ putStrLn $ showInfos color name
+
+
+en: It will compile and execute.
+en: In fact you can replace Name, Color and String everywhere.
+en: The compiler will treat them as completely identical.
+fr: Le code sera compilé et exécuté.
+fr: En fait vous pouvez remplace Name, Color et String n'importe où.
+fr: Le compilateur les traitera comme si ils était complétement identiques.
+
+en: Another method is to create your own types using the keyword `data`.
+fr: Une autre méthode est de créer vos propres type avec le mot-clé `data`.
+
+> 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
+
+en: Now if you switch parameters of `showInfos`, the compiler complains!
+en: So this is a potential mistake you will never make again and the only price is to be more verbose.
+fr: Maintenant, si vous échangez les paramètres de `showInfos`, le compilateur se plaint!
+fr: Au seul prix d'être plus verbeux, vous écartez définitivement cette erreur potentielle.
+
+en: Also notice that constructors are functions:
+fr: Remarquez aussi que les constructeurs sont des fonctions :
+
+
+NameConstr :: String -> Name
+ColorConstr :: String -> Color
+
+
+en: The syntax of `data` is mainly:
+fr: La syntaxe de `data` est principalement:
+
+
+data TypeName = ConstructorName [types]
+ | ConstructorName2 [types]
+ | ...
+
+
+en: Generally the usage is to use the same name for the
+en: DataTypeName and DataTypeConstructor.
+fr: Généralement on utilise le même nom pour le DatatTypeName et le DataTypeConstructor.
+
+en: Example:
+fr: Exemple :
+
+
+data Complex a = Num a => Complex a a
+
+
+en: Also you can use the record syntax:
+fr: Vous pouvez également utiliser cette syntaxe :
+
+
+data DataTypeName = DataConstructor {
+ field1 :: [type of field1]
+ , field2 :: [type of field2]
+ ...
+ , fieldn :: [type of fieldn] }
+
+
+en: And many accessors are made for you.
+en: Furthermore you can use another order when setting values.
+fr: Et de nombreux accesseurs sont définis pour vous.
+fr: En outre, vous pouvez utiliser une autre ordre lorsque vous définissez des valeurs.
+
+en: Example:
+fr: Exemple :
+
+
+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
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs
new file mode 100644
index 0000000..38de0c0
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/23_Types.lhs
@@ -0,0 +1,50 @@
+en:
Recursive type
+fr:
Type récursif
+
+en: You already encountered a recursive type: lists.
+en: You can re-create lists, but with a more verbose syntax:
+fr: Nous avons déjà rencontré un type récursif : les listes.
+fr: Nous pourrions re-créer les listes, avec une syntaxe plus bavarde:
+
+
+data List a = Empty | Cons a (List a)
+
+
+
+en: If you really want to use an easier syntax you can use an infix name for constructors.
+fr: Si vous voulez réellement utiliser une syntxe plus simple, utilisez un nom infixe pour les constructeurs.
+
+
+infixr 5 :::
+data List a = Nil | a ::: (List a)
+
+
+en: The number after `infixr` gives the precedence.
+fr: Le nombre après `infixr` donne la priorité.
+
+en: 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.
+fr: Si vous voulez pouvoir écrire (`Show`), lire (`Read`), tester l'égalité (`Eq`) et comparer (`Ord`) votre nouvelle structure, vous pouvez demander à Haskell de dériver les fonctions appropriées pour vous.
+
+> infixr 5 :::
+> data List a = Nil | a ::: (List a)
+> deriving (Show,Read,Eq,Ord)
+
+en: When you add `deriving (Show)` to your data declaration, Haskell creates a `show` function for you.
+en: We'll see soon how you can use your own `show` function.
+fr: Quand vous ajoutez `deriving (Show)` à votre déclaration, Haskell crée une fonction `show` pour vous.
+fr: Nous verrons bientôt comment utiliser sa propre fonction `show`.
+
+> convertList [] = Nil
+> convertList (x:xs) = x ::: convertList xs
+
+> main = do
+> print (0 ::: 1 ::: Nil)
+> print (convertList [0,1])
+
+en: This prints:
+fr: Ceci donne :
+
+~~~
+0 ::: (1 ::: Nil)
+0 ::: (1 ::: Nil)
+~~~
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs
new file mode 100644
index 0000000..3441406
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/30_Trees.lhs
@@ -0,0 +1,49 @@
+en:
Trees
+fr:
Les arbres
+
+blogimage("magritte-l-arbre.jpg","Magritte, l'Arbre")
+
+en: We'll just give another standard example: binary trees.
+fr: Voici une autre exemple standard : les arbres binaires.
+
+> import Data.List
+>
+> data BinTree a = Empty
+> | Node a (BinTree a) (BinTree a)
+> deriving (Show)
+
+en: We will also create a function which turns a list into an ordered binary tree.
+fr: Créons aussi une fonctions qui transforme une liste en un arbre binaire ordonné.
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+en: Look at how elegant this function is.
+en: In plain English:
+fr: Remarquez à quel point cette fonction est élégante.
+fr: En français :
+
+en: - an empty list will be converted to an empty tree.
+en: - a list `(x:xs)` will be converted to a tree where:
+en: - The root is `x`
+en: - Its left subtree is the tree created from members of the list `xs` which are strictly inferior to `x` and
+en: - the right subtree is the tree created from members of the list `xs` which are strictly superior to `x`.
+fr: - une liste vide est convertie en un arbre vide
+fr: - une liste `(x:xs)` sera convertie en un arbre où :
+fr: - La racine est `x`
+fr: - Le "sous-arbre" de gauche est l'arbre créé à partir des membres de la liste `xs` strictement inférieurs à `x`
+fr: - Le "sous-arbre" de droite est l'arbre créé à partir des membres de la liste `xs` strictement superieurs à `x`
+
+> main = print $ treeFromList [7,2,4,8]
+
+en: You should obtain the following:
+fr: Vous devriez obtenir :
+
+~~~
+Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)
+~~~
+
+en: This is an informative but quite unpleasant representation of our tree.
+fr: C'est une représentation de notre arbre informative mais plutôt déplaisante.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs
new file mode 100644
index 0000000..7a90d6a
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/31_Trees.lhs
@@ -0,0 +1,231 @@
+en: Just for fun, let's code a better display for our trees.
+en: I simply had fun making a nice function to display trees in a general way.
+en: You can safely skip this part if you find it too difficult to follow.
+fr: Juste pour le plaisir, codons un meilleur affichage pour nos arbres.
+fr: Je me suis simplement amusé à faire une belle fonction pour afficher les arbres de façon générale.
+fr: Vous pouvez passer cette partie si vous la trouvez difficile à suivre.
+
+en: We have a few changes to make.
+en: We remove the `deriving (Show)` from the declaration of our `BinTree` type.
+en: 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.
+fr: Nous avons quelques changements à faire.
+fr: Enlevons le `deriving (Show)` de la déclaration de notre type `BinTree`.
+fr: Il serait aussi utile de faire de BinTree une instance de (`Eq` et `Ord`), nous serons ainsi capable de tester l'égalité et de comparer des arbres.
+
+> data BinTree a = Empty
+> | Node a (BinTree a) (BinTree a)
+> deriving (Eq,Ord)
+
+en: Without the `deriving (Show)`, Haskell doesn't create a `show` method for us.
+en: We will create our own version of `show`.
+en: To achieve this, we must declare that our newly created type `BinTree a`
+en: is an instance of the type class `Show`.
+en: The general syntax is:
+fr: Sans le `deriving (Show)`, Haskell ne crée pas de méthode `show` pour nous.
+fr: Nous allons créer notre propre version.
+fr: Pour accomplir cela, nous devons déclarer que notre type `BinTree a`
+fr: est une instance de la classe de type `Show`.
+fr: La syntaxe générale est :
+
+
+instance Show (BinTree a) where
+en: show t = ... -- You declare your function here
+fr: show t = ... -- Déclarez votre fonction ici
+
+
+en: Here is my version of how to show a binary tree.
+en: Don't worry about the apparent complexity.
+en: I made a lot of improvements in order to display even stranger objects.
+fr: Voici ma version pour afficher un arbre binaire.
+fr: Ne vous inquiétez pas de sa complexité apparente.
+fr: J'ai fait beaucoup d'améliorations pour afficher même les objets les plus étranges.
+
+> -- 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"
+
+
+en: The `treeFromList` method remains identical.
+fr: La méthode `treeFromList` reste identique.
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+en: And now, we can play:
+fr: Et maintenant, nous pouvons jouer :
+
+> main = do
+> putStrLn "Int binary tree:"
+> print $ treeFromList [7,2,4,8,1,3,6,21,12,23]
+
+~~~
+en: Int binary tree:
+fr: Arbre binaire d'Int:
+< 7
+: |--2
+: | |--1
+: | `--4
+: | |--3
+: | `--6
+: `--8
+: `--21
+: |--12
+: `--23
+~~~
+
+en: Now it is far better!
+en: The root is shown by starting the line with the `<` character.
+en: And each following line starts with a `:`.
+en: But we could also use another type.
+fr: Maintenant c'est beaucoup mieux !
+fr: La racine est montrée en commençant la ligne avec le caractère `<`.
+fr: Et chaque ligne suivante est commence par `:`.
+fr: Mais nous pourrions aussi utiliser un autre type.
+
+> putStrLn "\nString binary tree:"
+> print $ treeFromList ["foo","bar","baz","gor","yog"]
+
+~~~
+en: String binary tree:
+fr: Arbre binaire de chaînes de caractères
+< "foo"
+: |--"bar"
+: | `--"baz"
+: `--"gor"
+: `--"yog"
+~~~
+
+en: As we can test equality and order trees, we can
+en: make tree of trees!
+fr: Commme nous pouvons tester l'égalité et ordonner des arbres,
+fr: nous pouvons aussi faire des arbres d'arbres!
+
+> putStrLn "\nBinary tree of Char binary trees:"
+> print ( treeFromList
+> (map treeFromList ["baz","zara","bar"]))
+
+~~~
+en: Binary tree of Char binary trees:
+fr: Arbre binaire d'arbres binaires de Char :
+< < 'b'
+: : |--'a'
+: : `--'z'
+: |--< 'b'
+: | : |--'a'
+: | : `--'r'
+: `--< 'z'
+: : `--'a'
+: : `--'r'
+~~~
+
+en: This is why I chose to prefix each line of tree display by `:` (except for the root).
+fr: C'est pour cela que j'ai choisi de préfixer chaque ligne par un `:` (sauf pour la racine).
+
+blogimage("yo_dawg_tree.jpg","Yo Dawg Tree")
+
+> putStrLn "\nTree of Binary trees of Char binary trees:"
+> print $ (treeFromList . map (treeFromList . map treeFromList))
+> [ ["YO","DAWG"]
+> , ["I","HEARD"]
+> , ["I","HEARD"]
+> , ["YOU","LIKE","TREES"] ]
+
+en: Which is equivalent to
+fr: Qui est équivalent à
+
+
+print ( treeFromList (
+ map treeFromList
+ [ map treeFromList ["YO","DAWG"]
+ , map treeFromList ["I","HEARD"]
+ , map treeFromList ["I","HEARD"]
+ , map treeFromList ["YOU","LIKE","TREES"] ]))
+
+
+en: and gives:
+fr: et donne :
+
+~~~
+en: Binary tree of Binary trees of Char binary trees:
+fr: Arbre d'arbres d'arbres de Char :
+< < < 'Y'
+: : : `--'O'
+: : `--< 'D'
+: : : |--'A'
+: : : `--'W'
+: : : `--'G'
+: |--< < 'I'
+: | : `--< 'H'
+: | : : |--'E'
+: | : : | `--'A'
+: | : : | `--'D'
+: | : : `--'R'
+: `--< < 'Y'
+: : : `--'O'
+: : : `--'U'
+: : `--< 'L'
+: : : `--'I'
+: : : |--'E'
+: : : `--'K'
+: : `--< 'T'
+: : : `--'R'
+: : : |--'E'
+: : : `--'S'
+~~~
+
+en: Notice how duplicate trees aren't inserted;
+en: there is only one tree corresponding to `"I","HEARD"`.
+en: We have this for (almost) free, because we have declared Tree to be an instance of `Eq`.
+fr: Remarquez que les arbres en double ne sont pas insérés.
+fr: Il n'y a qu'un seul arbre correspondant à `"I","HEARD"`.
+fr: Nous avons ceci presque gratuitement, car nous avons déclaré Tree comme instance de `Eq`.
+
+en: See how awesome this structure is:
+en: We can make trees containing not only integers, strings and chars, but also other trees.
+en: And we can even make a tree containing a tree of trees!
+fr: Voyez à quel point cette structure est formidable :
+fr: Nous pouvons faire des arbres contenant seulement des entiers, des chaînes de caractères, mais aussi d'autres arbres.
+fr: Et nous pouvons même faire un arbre contenant un arbre d'arbres!
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs
new file mode 100644
index 0000000..e8bf462
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/40_Infinites_Structures.lhs
@@ -0,0 +1,59 @@
+en:
Infinite Structures
+fr:
Structures infinies
+
+blogimage("escher_infinite_lizards.jpg","Escher")
+
+en: It is often said that Haskell is _lazy_.
+fr: On dit souvent que Haskell est _paresseux_.
+
+en: In fact, if you are a bit pedantic, you should say that [Haskell is _non-strict_](http://www.haskell.org/haskellwiki/Lazy_vs._non-strict).
+en: Laziness is just a common implementation for non-strict languages.
+fr: En fait, si vous êtes un petit peu pédant, vous devriez dire que [Haskell est _non-strict_](http://www.haskell.org/haskellwiki/Lazy_vs._non-strict) (_NDT: En anglais, pour changer_).
+fr: La paresse est juste une implémentation commune aux langages non-stricts.
+
+en: Then what does "not-strict" mean? From the Haskell wiki:
+fr: Alors que signifie "non-strict"? D'après le wiki de Haskell :
+
+en: > Reduction (the mathematical term for evaluation) proceeds from the outside in.
+en: >
+en: > so if you have `(a+(b*c))` then you first reduce `+` first, then you reduce the inner `(b*c)`
+fr: > La réduction (terme mathématique pour "évaluation") procède depuis l'extérieur.
+fr: >
+fr: > Donc si vous avez `(a+(b*c))`, alors vous réduisez `+` d'abord, puis vous réduisez `(b*c)`
+
+en: For example in Haskell you can do:
+fr: Par exemple en Haskell vous pouvez faire :
+
+> -- 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
+
+en: And it stops.
+fr: Et ça s'arrête.
+
+en: How?
+fr: Comment ?
+
+en: Instead of trying to evaluate `numbers` entirely,
+en: it evaluates elements only when needed.
+fr: Au lieu d'essayer d'évaluer `numbers` entièrement,
+fr: Haskell évalue les éléments seulement lorsque c'est nécessaire.
+
+en: Also, note in Haskell there is a notation for infinite lists
+fr: Remarquez aussi qu'en Haskell, il y a une notation pour les listes infinies
+
+~~~
+[1..] ⇔ [1,2,3,4...]
+[1,3..] ⇔ [1,3,5,7,9,11...]
+~~~
+
+en: and most functions will work with them.
+en: Also, there is a built-in function `take` which is equivalent to our `take'`.
+fr: et que la majorité des fonctions fonctionnera avec ces listes.
+fr: Il y a aussi une fonction `take` équivalente à notre `take'`.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs
new file mode 100644
index 0000000..f8384ff
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/02_Hard_Part/41_Infinites_Structures.lhs
@@ -0,0 +1,173 @@
+
+
+
+This code is mostly the same as the previous one.
+
+> import Debug.Trace (trace)
+> 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"
+>
+
+
+
+en: Suppose we don't mind having an ordered binary tree.
+en: Here is an infinite binary tree:
+fr: Supposons que nous ne nous préoccupions pas d'avoir une arbre ordonné.
+fr: Voici un arbre binaire infini :
+
+> nullTree = Node 0 nullTree nullTree
+
+en: A complete binary tree where each node is equal to 0.
+en: Now I will prove you can manipulate this object using the following function:
+fr: Un arbre complet où chaque noeud est égal à 0.
+fr: Maintenant je vais vous prouver que nous pouvons manipuler cet arbre avec la fonction suivante :
+
+> -- 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
+
+en: See what occurs for this program:
+fr: Regardez ce qui se passe avec ce programme :
+
+
+main = print $ treeTakeDepth 4 nullTree
+
+
+en: This code compiles, runs and stops giving the following result:
+fr: Le code compile, se lance et s'arrête en donnant ce résultat :
+
+~~~
+< 0
+: |-- 0
+: | |-- 0
+: | | |-- 0
+: | | `-- 0
+: | `-- 0
+: | |-- 0
+: | `-- 0
+: `-- 0
+: |-- 0
+: | |-- 0
+: | `-- 0
+: `-- 0
+: |-- 0
+: `-- 0
+~~~
+
+en: Just to heat up your neurones a bit more,
+en: let's make a slightly more interesting tree:
+fr: Pour nous chauffer encore un peu les neurones,
+fr: faisons un arbre plus intéressant :
+
+> 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)
+
+en: Another way to create this tree is to use a higher order function.
+en: This function should be similar to `map`, but should work on `BinTree` instead of list.
+en: Here is such a function:
+fr: Un autre moyen de créer cet arbre est d'utiliser une fonction d'ordre supérieur.
+fr: Cette fonction devrait être similaire à `map` n, mais devrait travailler sur un `BinTree` au lieu d'une liste.
+fr: Voici cette fonction :
+
+> -- 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)
+
+en: _Hint_: I won't talk more about this here.
+en: If you are interested in the generalization of `map` to other data structures,
+en: search for functor and `fmap`.
+fr: _NB_: Je ne parlerai pas plus de cette fonction ici.
+fr: Si vous vous intéressez à la généralisation de `map` à d'autres structures de données,
+fr: cherchez des informations sur les foncteurs et `fmap`.
+
+en: Our definition is now:
+fr: Notre définition est maintenant :
+
+> infTreeTwo :: BinTree Int
+> infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
+> (treeMap (\x -> x+1) infTreeTwo)
+
+en: Look at the result for
+fr: Regardez le résultat pour
+
+
+main = print $ treeTakeDepth 4 infTreeTwo
+
+
+~~~
+< 0
+: |-- -1
+: | |-- -2
+: | | |-- -3
+: | | `-- -1
+: | `-- 0
+: | |-- -1
+: | `-- 1
+: `-- 1
+: |-- 0
+: | |-- -1
+: | `-- 1
+: `-- 2
+: |-- 1
+: `-- 3
+~~~
+
+
+
+
+> main = do
+> print $ treeTakeDepth 4 nullTree
+> print $ treeTakeDepth 4 infTreeTwo
+
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs
new file mode 100644
index 0000000..7186e7a
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/00_Introduction.lhs
@@ -0,0 +1,28 @@
+en:
Hell Difficulty Part
+fr:
Partie de difficulté infernale
+
+en: Congratulations for getting so far!
+en: Now, some of the really hardcore stuff can start.
+fr: Félicitations pour être allé si loin!
+fr: Maitenant, les choses vraiment extrêmes peuvent commencer.
+
+en: If you are like me, you should get the functional style.
+en: You should also understand a bit more the advantages of laziness by default.
+en: But you also don't really understand where to start in order to make a real
+en: program.
+en: And in particular:
+fr: Si vous êtes comme moi, vous êtes déjà familier avec le style fonctionnel.
+fr: Vous devriez également comprendre les avantages de la paresse par défaut.
+fr: Mais vous ne comprenez peut-être pas vraiment par où commencer pour faire un vrai
+fr: programme.
+fr: Et en particulier :
+
+en: - How do you deal with effects?
+en: - Why is there a strange imperative-like notation for dealing with IO?
+fr: - Comment s'occuper des effets ?
+fr: - Pourquoi y a t-il une étrange notation impérative lorsque l'on s'occupe de l'Entrée/Sortie? (E/S, _IO_ pour _Input/Output_ en anglais)
+
+en: Be prepared, the answers might be complex.
+en: But they are all very rewarding.
+fr: Accrochez-vous, les réponses risquent d'être compliquées.
+fr: Mais elles en valent la peine.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs
new file mode 100644
index 0000000..5d64772
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/01_progressive_io_example.lhs
@@ -0,0 +1,117 @@
+en:
Deal With IO
+fr:
S'occuper de l'E/S (IO)
+
+blogimage("magritte_carte_blanche.jpg","Magritte, Carte blanche")
+
+ > %tldr
+ >
+en: > A typical function doing `IO` looks a lot like an imperative program:
+fr: > Une fonction typique qui fait de l'`IO` ressemble à un programme impératif:
+ >
+ > ~~~
+ > f :: IO a
+ > f = do
+ > x <- action1
+ > action2 x
+ > y <- action3
+ > action4 x y
+ > ~~~
+ >
+en: > - To set a value to an object we use `<-` .
+en: > - The type of each line is `IO *`;
+en: > in this example:
+fr: > - Pour définir la valeur d'un objet on utilise `<-` .
+fr: > - Le type de chaque ligne est `IO *`;
+fr: > dans cet exemple:
+ > - `action1 :: IO b`
+ > - `action2 x :: IO ()`
+ > - `action3 :: IO c`
+ > - `action4 x y :: IO a`
+ > - `x :: b`, `y :: c`
+en: > - Few objects have the type `IO a`, this should help you choose.
+en: > In particular you cannot use pure functions directly here.
+en: > To use pure functions you could do `action2 (purefunction x)` for example.
+fr: > - Quelques objets ont le type `IO a`, cela devrait vous aider à choisir.
+fr: > En particulier vous ne pouvez pas utiliser de fonctions pures directement ici.
+fr: > Pour utiliser des fonctions pures vous pourriez faire `action2 (pureFunction x)` par exemple.
+
+en: In this section, I will explain how to use IO, not how it works.
+en: You'll see how Haskell separates the pure from the impure parts of the program.
+fr: Dans cette section, je vais expliquer comment utiliser l'IO, pas comment ça marche.
+fr: Vous verrez comment Haskell sépare les parties pures et impures du programme.
+
+en: Don't stop because you're trying to understand the details of the syntax.
+en: Answers will come in the next section.
+fr: Ne vous arrêtez pas sur les détails de la syntaxe
+fr: Les réponses viendront dans la section suivante.
+
+en: What to achieve?
+fr: Que cherchons-nous à faire?
+
+en: > Ask a user to enter a list of numbers.
+en: > Print the sum of the numbers
+fr: > Demander une liste de nombres à l'utilisateur.
+fr: > Afficher la somme de ces nombres.
+
+> toList :: String -> [Integer]
+> toList input = read ("[" ++ input ++ "]")
+>
+> main = do
+> putStrLn "Enter a list of numbers (separated by comma):"
+> input <- getLine
+> print $ sum (toList input)
+
+en: It should be straightforward to understand the behavior of this program.
+en: Let's analyze the types in more detail.
+fr: Il devrait être simple de comprendre le comportement de ce programme.
+fr: Analysons les types en détails.
+
+~~~
+putStrLn :: String -> IO ()
+getLine :: IO String
+print :: Show a => a -> IO ()
+~~~
+
+en: Or more interestingly, we note that each expression in the `do` block has a type of `IO a`.
+fr: Ou, de manièree plus intéressante, on remarque que chaque expression dans le bloc `do` est de type `IO a`.
+
+
+
+en: We should also pay attention to the effect of the `<-` symbol.
+fr: Nous devrions aussi prêter attention à l'effet du symbole `<-`.
+
+~~~
+do
+ x <- something
+~~~
+
+en: If `something :: IO a` then `x :: a`.
+fr: Si `something :: IO a` alors `x :: a`.
+
+en: Another important note about using `IO`:
+en: All lines in a do block must be of one of the two forms:
+fr: Une autre remarque importante sur l'`IO`:
+fr: Toutes les lignes d'un bloc `do` doivent être d'une des deux formes suivantes :
+
+~~~
+action1 :: IO a
+ -- in this case, generally a = ()
+~~~
+
+ou
+
+~~~
+value <- action2 -- where
+ -- action2 :: IO b
+ -- value :: b
+~~~
+
+en: These two kinds of line will correspond to two different ways of sequencing actions.
+en: The meaning of this sentence should be clearer by the end of the next section.
+fr: Ces deux types de ligne correspondent à deux types différents de séquençage d'action.
+fr: La signification de cette phrase devrait être plus claire à la fin de la prochaine section.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs
new file mode 100644
index 0000000..4feec04
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/02_progressive_io_example.lhs
@@ -0,0 +1,123 @@
+en: Now let's see how this program behaves.
+en: For example, what happens if the user enters something strange?
+en: Let's try:
+fr: Maintenant voyons comment ce programme se comporte.
+fr: Par exemple, que ce passe-t-il si l'utilisateur entre une mauvaise valeur?
+fr: Essayons :
+
+~~~
+ % runghc 02_progressive_io_example.lhs
+ Enter a list of numbers (separated by comma):
+ foo
+ Prelude.read: no parse
+~~~
+
+
+en: Argh! An evil error message and a crash!
+fr: Argh! Un message d'erreur effrayant et un crash !
+en: Our first improvement will simply be to answer with a more friendly message.
+fr: Notre première amélioration sera de répondre avec un message plus amical.
+
+en: In order to do this, we must detect that something went wrong.
+fr: Pour faire cela, nous devons détecter que quelque chose s'est mal passé.
+en: Here is one way to do this: use the type `Maybe`.
+fr: Voici un moyen de le faire : utiliser le type `Maybe`.
+en: This is a very common type in Haskell.
+fr: C'est un type très utilisé en Haskell.
+
+> import Data.Maybe
+
+en: What is this thing? `Maybe` is a type which takes one parameter.
+fr: Mais qu'est-ce que c'est ? `Maybe` est un type qui prend un paramètre.
+en: Its definition is:
+fr: Sa définition est :
+
+
+data Maybe a = Nothing | Just a
+
+
+fr: C'est un bon moyen de dire qu'il y a eu une erreur en essayant de créer/évaluer
+en: This is a nice way to tell there was an error while trying to create/compute
+fr: une valeur.
+en: a value.
+fr: La fonction `maybeRead` en est un bon exemple.
+en: The `maybeRead` function is a great example of this.
+fr: C'est une fonction similaire à `read`[^1],
+en: This is a function similar to the function `read`[^1],
+fr: mais s'il y a un problème, la valeur retournée est `Nothing`.
+en: but if something goes wrong the returned value is `Nothing`.
+fr: Si la valeur est bonne, la valeur retournée est `Just `.
+en: If the value is right, it returns `Just `.
+fr: Ne vous efforcez pas trop de comprendre cette fonction.
+en: Don't try to understand too much of this function.
+fr: J'utilise une fonction de plus bas niveau que `read` : `reads`.
+en: I use a lower level function than `read`: `reads`.
+
+fr: [^1]: Qui est elle-même très similaire à la fonction `eval` de javascript, appliquée sur une chaîne contenant du code au format JSON.
+en: [^1]: Which is itself very similar to the javascript `eval` function, that is applied to a string containing JSON.
+
+> maybeRead :: Read a => String -> Maybe a
+> maybeRead s = case reads s of
+> [(x,"")] -> Just x
+> _ -> Nothing
+
+fr: Maintenant, pour être plus lisible, on définit une fonction comme ceci :
+en: Now to be a bit more readable, we define a function which goes like this:
+fr: Si la chaîne a un mauvais format, elle retournera `Nothing`.
+en: If the string has the wrong format, it will return `Nothing`.
+fr: Sinon, par exemple pour "1,2,3", cela retournera `Just [1,2,3]`.
+en: Otherwise, for example for "1,2,3", it will return `Just [1,2,3]`.
+
+> getListFromString :: String -> Maybe [Integer]
+> getListFromString str = maybeRead $ "[" ++ str ++ "]"
+
+
+en: We simply have to test the value in our main function.
+fr: Nous avons juste à tester la valeur dans notre fonction principale.
+
+> 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."
+
+fr: En cas d'erreur, on affiche un joli message.
+en: In case of error, we display a nice error message.
+
+fr: Notez que le type de chaque expression dans le bloc `do` de `main` reste de la forme `IO a`.
+en: Note that the type of each expression in the main's `do` block remains of the form `IO a`.
+fr: La seule construction étrange est `error`.
+en: The only strange construction is `error`.
+fr: Disons juste que `error msg` prend le type nécessaire (ici, `IO ()`).
+en: I'll just say here that `error msg` takes the needed type (here `IO ()`).
+
+fr: Une chose très importante à noter est le type de toutes les fonctions définies jusqu'ici.
+en: One very important thing to note is the type of all the functions defined so far.
+fr: Il n'y a qu'une seule fonction qui contient `IO` dans son type : `main`.
+en: There is only one function which contains `IO` in its type: `main`.
+fr: Cela signifie que `main` est impure.
+en: This means main is impure.
+fr: Mais `main` utilise `getListFromString`, qui, elle, est pure.
+en: But main uses `getListFromString` which is pure.
+fr: Nous pouvons donc facilement repérer quelles fonctions sont pures
+en: So it's clear just by looking at declared types which functions are pure and
+fr: et lesquelles sont impures, seulement en regardant leur type.
+en: which are impure.
+
+fr: Pourquoi la pureté a-t-elle de l'importance?
+en: Why does purity matter?
+fr: Parmi ses nombreux avantages, en voici trois :
+en: Among the many advantages, here are three:
+
+fr: - Il est beaucoup plus facile de penser à du code pur qu'à du code impur.
+en: - It is far easier to think about pure code than impure code.
+fr: - La pureté vous protège de tous les bugs difficiles à reproduire dûs aux [effets de bord](https://fr.wikipedia.org/wiki/Effet_de_bord_(informatique)).
+en: - Purity protects you from all the hard-to-reproduce bugs that are due to side effects.
+fr: - Vous pouvez évaluer des fonctions pures dans n'importe quel ordre ou en parallèle, sans prendre de risques.
+en: - You can evaluate pure functions in any order or in parallel without risk.
+
+fr: C'est pourquoi vous devriez mettre le plus de code possible dans des fonctions pures.
+en: This is why you should generally put as most code as possible inside pure functions.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs
new file mode 100644
index 0000000..8293a83
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/03_progressive_io_example.lhs
@@ -0,0 +1,80 @@
+fr: La prochaine étape sera de demander la liste de nombres à l'utilisateur encore et encore jusqu'à ce qu'il entre une réponse valide.
+en: Our next iteration will be to prompt the user again and again until she enters a valid answer.
+
+fr: Nous gardons la première partie :
+en: We keep the first part:
+
+> 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 ++ "]"
+
+fr: Maintenant nous créons la fonction qui demandera une liste d'entiers à l'utilisateur
+en: Now we create a function which will ask the user for an list of integers
+fr: jusqu'à ce que l'entrée soit correcte
+en: until the input is right.
+
+> 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
+
+fr: Cette fonction est de type `IO [Integer]`.
+en: This function is of type `IO [Integer]`.
+fr: Cela signifie que la valeur récupérée est de type `[Integer`] et est le résultat d'actions d'E/S.
+en: Such a type means that we retrieved a value of type `[Integer]` through some IO actions.
+fr: D'aucuns diront avec enthousiasme :
+en: Some people might explain while waving their hands:
+
+fr: > «C'est un `[Integer]` dans un `IO` !»
+en: > «This is an `[Integer]` inside an `IO`»
+
+fr: Si vous voulez comprendre les détails derrière tout cela, vous devrez lire la prochaine section.
+en: If you want to understand the details behind all of this, you'll have to read the next section.
+fr: Mais si vous voulez seulement _utiliser_ l'E/S, contentez-vous pratiquer un peu et rappelez-vous de penser aux types.
+en: But really, if you just want to _use_ IO just practice a little and remember to think about the type.
+
+fr: Finalement, notre fonction `main`est bien plus simple :
+en: Finally our main function is much simpler:
+
+> main :: IO ()
+> main = do
+> list <- askUser
+> print $ sum list
+
+fr: Nous avons fini notre introduction à l'`IO`.
+en: We have finished with our introduction to `IO`.
+fr: C'était plutôt rapide. Voici les principales choses à retenir :
+en: This was quite fast. Here are the main things to remember:
+
+fr: - Dans le bloc `do`, chaque expression doit avoir le type `IO a`.
+en: - in the `do` block, each expression must have the type `IO a`.
+fr: Vous êtes donc limité quant au panel d'expression disponibles.
+en: You are then limited with regard to the range of expressions available.
+fr: Par exemple, `getLine`, `print`, `putStrLn`, etc...
+en: For example, `getLine`, `print`, `putStrLn`, etc...
+fr: - Essayez d'externaliser le plus possible les fonctions pures.
+en: - Try to externalize the pure functions as much as possible.
+fr: - le type `IO a` signifie : une _action_ d'E/S qui retourne un élément de type a.
+en: - the `IO a` type means: an IO _action_ which returns an element of type `a`.
+fr: L'`IO` représente des actions; sous le capot, `IO a` est le type d'une fonction.
+en: `IO` represents actions; under the hood, `IO a` is the type of a function.
+fr: Lisez la prochaine section si vous êtes curieux.
+en: Read the next section if you are curious.
+
+fr: Si vous pratiquez un peu, vous devriez être capable d'_utiliser_ l'`IO`.
+en: If you practice a bit, you should be able to _use_ `IO`.
+
+fr: > -Exercices_:
+en: > _Exercises_:
+ >
+fr: > - Écrivez un programme qui additionne tous ses arguments. Utilisez la fonction `getArgs`.
+en: > - Make a program that sums all of its arguments. Hint: use the function `getArgs`.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs
new file mode 100644
index 0000000..6cfd73f
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/20_Detailled_IO.lhs
@@ -0,0 +1,549 @@
+fr:
Le truc des IO révélé
+en:
IO trick explained
+
+blogimage("magritte_pipe.jpg","Magritte, ceci n'est pas une pipe")
+
+fr: > Voici un %tlal pour cette section.
+en: > Here is a %tldr for this section.
+ >
+fr: > Pour séparer les parties pures et impures,
+en: > To separate pure and impure parts,
+fr: > `main` est définie comme une fonction.
+en: > `main` is defined as a function
+fr: > qui modifie l'état du monde.
+en: > which modifies the state of the world.
+ >
+ > ~~~
+ > main :: World -> World
+ > ~~~
+ >
+fr: > Une fonction aura des effets de bord si elle a ce type.
+en: > A function is guaranteed to have side effects only if it has this type.
+fr: > Mais regardez cette fonction `main` typique:
+en: > But look at a typical main function:
+ >
+ > ~~~
+ >
+ > 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
+ > ~~~
+ >
+fr: > Nous avons beaucoup d'élements temporaires (ici, `w1`, `w2` et `w3`)
+en: > We have a lot of temporary elements (here `w1`, `w2` and `w3`)
+fr: > qui doivent être passés à l'action suivante.
+en: > which must be passed on to the next action.
+ >
+fr: > Nous créons une fonction `bind` ou `(>>=)`.
+en: > We create a function `bind` or `(>>=)`.
+fr: > Avec `bind` nous n'avons plus besoin de noms temporaires.
+en: > With `bind` we don't need temporary names anymore.
+ >
+ > ~~~
+ > main =
+ > action1 >>= action2 >>= action3 >>= action4
+ > ~~~
+ >
+fr: > Bonus: Haskell a du sucre syntaxique :
+en: > Bonus: Haskell has syntactical sugar for us:
+ >
+ > ~~~
+ > main = do
+ > v1 <- action1
+ > v2 <- action2 v1
+ > v3 <- action3 v2
+ > action4 v3
+ > ~~~
+
+
+fr: Pourquoi avons-nous utilisé cette syntaxe étrange, et quel est exactement le type `IO`?
+en: Why did we use this strange syntax, and what exactly is this `IO` type?
+fr: Cela peut sembler un peu magique.
+en: It looks a bit like magic.
+
+fr: Pour l'instant, oublions les parties pures de notre programme, et concentrons-nous
+en: For now let's just forget all about the pure parts of our program, and focus
+fr: sur les parties impures:
+en: on the impure parts:
+
+
+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
+
+
+fr: Première remarque : on dirait de l'impératif.
+en: First remark: this looks imperative.
+fr: Haskell est assez puissant pour faire sembler impératif du code impur.
+en: Haskell is powerful enough to make impure code look imperative.
+fr: Par exemple, si vous le vouliez vous pourriez créer une boucle `while` en Haskell.
+en: For example, if you wish you could create a `while` in Haskell.
+fr: En fait, pour utiliser les `IO`, le style impératif est en général plus approprié.
+en: In fact, for dealing with `IO`, an imperative style is generally more appropriate.
+
+fr: Mais vous devriez avoir remarqué que la notation est inhabituelle.
+en: But you should have noticed that the notation is a bit unusual.
+fr: Voici pourquoi, en détail.
+en: Here is why, in detail.
+
+fr: Dans un langage impur, l'état du monde peut être vu comme une énorme variable globale cachée.
+en: In an impure language, the state of the world can be seen as a huge hidden global variable.
+fr: Cette variable cachée est accessible par toutes les fonctions du langage.
+en: This hidden variable is accessible by all functions of your language.
+fr: Par exemple, vous pouvez lire et écrire dans un fichier avec n'importe quelle fonction.
+en: For example, you can read and write a file in any function.
+fr: Le fait que le fichier putatif existe ou non est une éventualité qui relève des états possibles que le monde courant peut prendre.
+en: Whether a file exists or not is a difference in the possible states that the world can take.
+
+fr: En Haskell l'état courant du monde n'est pas caché.
+en: In Haskell the current state of the world is not hidden.
+fr: Au contraire, il est dit _explicitement_ que `main` est une fonction qui change _potentiellement_ l'état du monde.
+en: Rather, it is _explicitly_ said that `main` is a function that _potentially_ changes the state of the world.
+fr: Son type est donc quelque chose comme :
+en: Its type is then something like:
+
+
+main :: World -> World
+
+
+fr: Les fonctions ne sont pas toutes susceptibles de modifier cette variable.
+en: Not all functions may access this variable.
+fr: Celle qui peuvent la modifier sont impures.
+en: Those which have access to this variable are impure.
+fr: Les fonctions qui ne peuvent pas agir sur la variable sont pures[^032001].
+en: Functions to which the world variable isn't provided are pure[^032001].
+
+fr: [^032001]: Il y a quelques exceptions _peu sûres_ à cette règle. Mais vous ne devriez pas en voir en application réelle, sauf pour le _debugging_.
+en: [^032001]: There are some _unsafe_ exceptions to this rule. But you shouldn't see such use in a real application except maybe for debugging purposes.
+
+fr: Haskell considère l'état du monde comme une variable à passer à `main`.
+en: Haskell considers the state of the world as an input variable to `main`.
+fr: Mais son type réel est plus proche de celui ci[^032002] :
+en: But the real type of main is closer to this one[^032002]:
+
+fr: [^032002]: Pour les curieux, le vrai type est `data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}`. Tous les `#` ont rapport avec l'optimisation et j'ai échangé quelques champs dans mon exemple. Mais c'est l'idée de base.
+en: [^032002]: 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.
+
+
+main :: World -> ((),World)
+
+
+fr: Le type `()` est le type "unit".
+en: The `()` type is the unit type.
+fr: Rien à voir ici.
+en: Nothing to see here.
+
+fr: Maintenant réécrivons notre fonction `main` avec cela à l'esprit :
+en: Now let's rewrite our main function with this in mind:
+
+
+main w0 =
+ let (list,w1) = askUser w0 in
+ let (x,w2) = print (sum list,w1) in
+ x
+
+
+fr: D'abord, on remarque que toutes les fonctions avec des effets de bord doivent avoir le type :
+en: First, we note that all functions which have side effects must have the type:
+
+
+World -> (a,World)
+
+
+fr: où `a` est le type du résultat.
+en: where `a` is the type of the result.
+fr: Par exemple, une fonction `getChar` aura le type `World -> (Char, World).
+en: For example, a `getChar` function should have the type `World -> (Char, World)`.
+
+fr: Une autre chose à noter est l'astuce pour corriger l'ordre d'évaluation.
+en: Another thing to note is the trick to fix the order of evaluation.
+fr: En Haskell, pour évaluer `f a b`, vous avez l'embarras du choix :
+en: In Haskell, in order to evaluate `f a b`, you have many choices:
+
+fr: - évaluer d'abord `a` puis `b` puis `f a b`
+en: - first eval `a` then `b` then `f a b`
+fr: - évaluer d'abord `b` puis `a` puis `f a b`
+en: - first eval `b` then `a` then `f a b`.
+fr: - évaluer `a` et `b` parallèlement, puis `f a b`
+en: - eval `a` and `b` in parallel then `f a b`
+
+fr: Cela vient du fait que nous avons recours à une partie pure du langage.
+en: This is true because we're working in a pure part of the language.
+
+fr: Maintenant, si vous regardez la fonction `main`, vous voyez tout de suite qu'il faut évaluer la première
+en: Now, if you look at the main function, it is clear you must eval the first
+fr: ligne avant la seconde, car pour évaluer la seconde ligne vous devez
+en: line before the second one since to evaluate the second line you have
+fr: utliser un paramètre donné suite à l'évaluation de la première ligne.
+en: to get a parameter given by the evaluation of the first line.
+
+fr: Cette astuce fonctionne très bien.
+en: This trick works like a charm.
+fr: Le compilateur donnera à chaque étape un pointeur sur l'id du nouveau monde courant.
+en: The compiler will at each step provide a pointer to a new real world id.
+fr: En réalité, `print` sera évaluée comme suit :
+en: Under the hood, `print` will evaluate as:
+
+fr: - Écrit quelque chose sur l'écran
+en: - print something on the screen
+fr: - Modifie l'id du monde
+en: - modify the id of the world
+fr: - renvoyer `((), id du nouveau monde)`.
+en: - evaluate as `((),new world id)`.
+
+fr: Maintenant, si jetez un oeil au style de la fonction `main`, vous remarquerez qu'il est clairement peu commode.
+en: Now, if you look at the style of the main function, it is clearly awkward.
+fr: Essayons de faire la même chose avec la fonction `askUser` :
+en: Let's try to do the same to the `askUser` function:
+
+
+askUser :: World -> ([Integer],World)
+
+
+fr: Avant :
+en: Before:
+
+
+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
+
+
+fr: Après :
+en: After:
+
+
+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)
+
+
+fr: C'est similaire, mais peu commode.
+en: This is similar, but awkward.
+fr: Voyez-vous toutes ces variables temporaires `w?`.
+en: Look at all these temporary `w?` names.
+
+fr: Voici la leçon : une implémentation naïve des IO dans les langages fonctionnels purs serait maladroite !
+en: The lesson is: naive IO implementation in Pure functional languages is awkward!
+
+fr: Heureusement, il y a un meilleur moyen de résoudre ce problème.
+en: Fortunately, there is a better way to handle this problem.
+fr: Nous voyons un motif.
+en: We see a pattern.
+fr: Chaque ligne est de la forme :
+en: Each line is of the form:
+
+
+let (y,w') = action x w in
+
+
+fr: Même si pour certaines lignes l'argument `x` n'est pas nécessaire.
+en: Even if for some lines the first `x` argument isn't needed.
+fr: La sortie est un couple, `(answer, newWorldValue)`.
+en: The output type is a couple, `(answer, newWorldValue)`.
+fr: Chaque fonction `f` doit avoir un type similaire à :
+en: Each function `f` must have a type similar to:
+
+
+f :: World -> (a,World)
+
+
+fr: Et ce n'est pas fini, nous pouvons aussi remarquer que nous suivons toujours le même motif :
+en: Not only this, but we can also note that we always follow the same usage pattern:
+
+
+let (y,w1) = action1 w0 in
+let (z,w2) = action2 w1 in
+let (t,w3) = action3 w2 in
+...
+
+
+fr: Chaque action peut prendre de 0 à n paramètres.
+en: Each action can take from 0 to n parameters.
+fr: Et en particulier, chaque action prend comme paramètre le résultat de la ligne précédente.
+en: And in particular, each action can take a parameter from the result of a line above.
+
+fr: Par exemple, nous pourrions aussi avoir :
+en: For example, we could also have:
+
+
+let (_,w1) = action1 x w0 in
+let (z,w2) = action2 w1 in
+let (_,w3) = action3 z w2 in
+...
+
+
+fr: Avec, bien entendu, `actionN w :: (World) -> (a,World)`.
+en: With, of course: `actionN w :: (World) -> (a,World)`.
+
+fr: > IMPORTANT: Il y a seulement 2 schémas importants à considérer :
+en: > IMPORTANT: there are only two important patterns to consider:
+ >
+ > ~~~
+ > let (x,w1) = action1 w0 in
+ > let (y,w2) = action2 x w1 in
+ > ~~~
+ >
+fr: > et
+en: > and
+ >
+ > ~~~
+ > let (_,w1) = action1 w0 in
+ > let (y,w2) = action2 w1 in
+ > ~~~
+
+leftblogimage("jocker_pencil_trick.jpg","Jocker pencil trick")
+
+fr: Maintenant, préparez-vous pour un petit tour de magie !
+en: Now, we will do a magic trick.
+fr: Faisons disparaître les variables temporaires de monde courant.
+en: We will make the temporary world symbols "disappear".
+fr: Nous allons `attacher` (_NDT: `bind` en anglais_) les deux lignes.
+en: We will `bind` the two lines.
+fr: Définissons la fonction `bind`.
+en: Let's define the `bind` function.
+fr: Son type est assez intimidant au début :
+en: Its type is quite intimidating at first:
+
+
+bind :: (World -> (a,World))
+ -> (a -> (World -> (b,World)))
+ -> (World -> (b,World))
+
+
+fr: Mais gardez en tête que `(World -> (a,World))` est le type d'une action d'IO.
+en: But remember that `(World -> (a,World))` is the type for an IO action.
+fr: Renommons-le pour plus de clarté :
+en: Now let's rename it for clarity:
+
+
+type IO a = World -> (a, World)
+
+
+fr: Quelques exemples de fonctions :
+en: Some examples of functions:
+
+
+getLine :: IO String
+print :: Show a => a -> IO ()
+
+
+fr: `getLine` est une action d'E/S qui prend le monde en paramètre et retourne un couple `(String, World)`.
+en: `getLine` is an IO action which takes world as a parameter and returns a couple `(String, World)`.
+fr: Cela peut être résumé par : `getLine` est de type `IO String`, que nous pouvons voir comme une action d'E/S qui retournera une chaîne de caractères "dans une E/S".
+en: 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".
+
+fr: La fonction `print` est elle aussi intéressante.
+en: The function `print` is also interesting.
+fr: Elle prend un argument qui peut être montré avec `show`.
+en: It takes one argument which can be shown.
+fr: En fait, elle prend deux arguments.
+en: In fact it takes two arguments.
+fr: Le premier est la valeur et le deuxième est l'état du monde.
+en: The first is the value to print and the other is the state of world.
+fr: Elle retourne un couple de type `((), World)`.
+en: It then returns a couple of type `((), World)`.
+fr: Cela signifie qu'elle change l'état du monde, mais ne produit pas d'autre donnée.
+en: This means that it changes the state of the world, but doesn't yield any more data.
+
+fr: Ce nouveau type `IO a` nous aide à simplifier le type de `bind` :
+en: This new `IO a` type helps us simplify the type of `bind`:
+
+
+bind :: IO a
+ -> (a -> IO b)
+ -> IO b
+
+
+fr: Cela dit que `bind` prend deux actions d'E/S en paramètres et retourne une autre action d'E/S.
+en: It says that `bind` takes two IO actions as parameters and returns another IO action.
+
+fr: Maintenant, rappelez-vous des motifs _importants_. Le premier était :
+en: Now, remember the _important_ patterns. The first was:
+
+
+pattern1 w0 =
+ let (x,w1) = action1 w0 in
+ let (y,w2) = action2 x w1 in
+ (y,w2)
+
+
+fr: Voyez les types :
+en: Look at the types:
+
+
+action1 :: IO a
+action2 :: a -> IO b
+pattern1 :: IO b
+
+
+fr: Cela ne vous semble-t-il pas familier ?
+en: Doesn't it seem familiar?
+
+
+(bind action1 action2) w0 =
+ let (x, w1) = action1 w0
+ (y, w2) = action2 x w1
+ in (y, w2)
+
+
+fr: L'idée est de cacher l'argument `World` avec cette fonction. Allons-y !
+en: The idea is to hide the World argument with this function. Let's go:
+fr: Par exemple si nous avions voulu simuler :
+en: As an example imagine if we wanted to simulate:
+
+
+let (line1, w1) = getLine w0 in
+let ((), w2) = print line1 in
+((), w2)
+
+
+fr: Maintenant, en utilisant la fonction `bind` :
+en: Now, using the `bind` function:
+
+
+(res, w2) = (bind getLine print) w0
+
+
+fr: Comme `print` est de type `Show a => a -> (World -> ((), World))`, nous savons que `res = ()` (type `unit`)
+en: As print is of type `Show a => a -> (World -> ((), World))`, we know `res = ()` (`unit` type).
+fr: Si vous ne voyez pas ce qui est magique ici, essayons avec trois lignes cette fois.
+en: If you didn't see what was magic here, let's try with three lines this time.
+
+
+
+let (line1,w1) = getLine w0 in
+let (line2,w2) = getLine w1 in
+let ((),w3) = print (line1 ++ line2) in
+((),w3)
+
+
+fr: Qui est équivalent à :
+en: Which is equivalent to:
+
+
+(res,w3) = (bind getLine (\line1 ->
+ (bind getLine (\line2 ->
+ print (line1 ++ line2))))) w0
+
+
+fr: Avez-vous remarqué quelque chose ?
+en: Didn't you notice something?
+fr: Oui, aucune variable `World` temporaire n'est utilisée !
+en: Yes, no temporary World variables are used anywhere!
+fr: C'est _MA_._GIQUE_.
+en: This is _MA_. _GIC_.
+
+fr: Nous pouvons utiliser une meilleure notation.
+en: We can use a better notation.
+fr: Utilisons `(>>=)` au lieu de `bind`.
+en: Let's use `(>>=)` instead of `bind`.
+fr: `(>>=)` est une fonction infixe, comme
+en: `(>>=)` is an infix function like
+fr: `(+)`; pour mémoire : `3 + 4 ⇔ (+) 3 4`
+en: `(+)`; reminder `3 + 4 ⇔ (+) 3 4`
+
+
+(res,w3) = (getLine >>=
+ (\line1 -> getLine >>=
+ (\line2 -> print (line1 ++ line2)))) w0
+
+
+fr: Ho Ho Ho! Joyeux Noël !
+fr; Haskell a confectionné du sucre syntaxique pour vous :
+en: Ho Ho Ho! Merry Christmas Everyone!
+en: Haskell has made syntactical sugar for us:
+
+
+do
+ x <- action1
+ y <- action2
+ z <- action3
+ ...
+
+
+fr: Est remplacé par :
+en: Is replaced by:
+
+
+action1 >>= (\x ->
+action2 >>= (\y ->
+action3 >>= (\z ->
+...
+)))
+
+
+fr: Remarquez que vous pouvez utliser `x` dans `action2` et `x` et `y` dans `action3`.
+en: Note that you can use `x` in `action2` and `x` and `y` in `action3`.
+
+fr: Mais que se passe-t-il pour les lignes qui n'utilisent pas le `<-` ?
+en: But what about the lines not using the `<-`?
+fr: Facile, une autre fonction `blindBind` :
+en: Easy, another function `blindBind`:
+
+
+blindBind :: IO a -> IO b -> IO b
+blindBind action1 action2 w0 =
+ bind action (\_ -> action2) w0
+
+
+fr: Je n'ai pas simplifié cette définition pour plus de clarté.
+en: I didn't simplify this definition for the purposes of clarity.
+fr: Bien sûr, nous pouvons utiliser une meilleure notation avec l'opérateur `(>>)`.
+en: Of course, we can use a better notation: we'll use the `(>>)` operator.
+
+fr: Et
+en: And
+
+
+do
+ action1
+ action2
+ action3
+
+
+fr: Devient
+en: Is transformed into
+
+
+action1 >>
+action2 >>
+action3
+
+
+fr: Enfin, une autre fonction est plutôt utile.
+en: Also, another function is quite useful.
+
+
+putInIO :: a -> IO a
+putInIO x = IO (\w -> (x,w))
+
+
+fr: D'une manière générale, c'est une façon de mettre des valeurs pures dans le "contexte d'E/S".
+en: This is the general way to put pure values inside the "IO context".
+fr: Le nom général pour `putInIO` est `return`.
+en: The general name for `putInIO` is `return`.
+fr: C'est un plutôt un mauvais nom lorsque vous commencez à programmer en Haskell. `return` est très différent de ce à quoi vous pourriez être habitué.
+en: This is quite a bad name when you learn Haskell. `return` is very different from what you might be used to.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs
new file mode 100644
index 0000000..f0c9dee
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/01_IO/21_Detailled_IO.lhs
@@ -0,0 +1,49 @@
+fr: Pour finir, traduisons notre exemple :
+en: To finish, let's translate our example:
+
+
+
+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
+
+
+fr: Se traduit en :
+en: Is translated into:
+
+> 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
+
+fr: Vous pouvez compiler ce code pour vérifier qu'il marche.
+en: You can compile this code to verify that it works.
+
+fr: Imaginez à quoi il ressemblerait sans le `(>>)` et `(>>=)`.
+en: Imagine what it would look like without the `(>>)` and `(>>=)`.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs
new file mode 100644
index 0000000..b658ee8
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/10_Monads.lhs
@@ -0,0 +1,124 @@
+fr:
Les monades
+en:
Monads
+
+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.")
+
+fr: Maintenant le secret peut être dévoilé : `IO` est une _monade_.
+en: Now the secret can be revealed: `IO` is a _monad_.
+fr: Être une monade signifie que vous avez accès à du sucre syntaxique avec la notation `do`.
+en: Being a monad means you have access to some syntactical sugar with the `do` notation.
+fr: Mais principalement, vous avez accès à un motif de codage qui tempérera le flux de votre code.
+en: But mainly, you have access to a coding pattern which will ease the flow of your code.
+
+fr: > **Remarques importantes** :
+en: > **Important remarks**:
+ >
+fr: > - Le monades n'ont pas forcément quoi que ce soit à voir avec les effets de bord !
+en: > - Monad are not necessarily about effects!
+fr: > Il y a beaucoup de monades _pures_.
+en: > There are a lot of _pure_ monads.
+fr: > - Les monades concernent plus le séquençage.
+en: > - Monad are more about sequencing
+
+fr: En Haskell, `Monad` est une classe de type.
+en: In Haskell, `Monad` is a type class.
+fr: Pour être une instance d'une classe de type, vous devez fournir les fonctions `(>>=)` et `return`.
+en: To be an instance of this type class, you must provide the functions `(>>=)` and `return`.
+fr: La fonction `(>>)` est dérivée de `(>>=)`.
+en: The function `(>>)` is derived from `(>>=)`.
+fr: Voici commment la classe de type `Monad` est déclarée (grosso modo) :
+en: Here is how the type class `Monad` is declared (basically):
+
+
+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
+
+fr: -- Vous pouvez ignorer cette fonction généralement,
+en: -- You should generally safely ignore this function
+fr: -- je crois qu'elle existe pour des raisons historiques
+en: -- which I believe exists for historical reasons
+ fail :: String -> m a
+ fail = error
+
+
+
+fr: > Remarques :
+en: > Remarks:
+ >
+fr: > - le mot-clé `class` n'est pas votre ami.
+en: > - the keyword `class` is not your friend.
+fr: > Une classe en Haskell _n'est pas_ du même genre que celle des langages orientés-objet.
+en: > A Haskell class is _not_ a class of the kind you will find in object-oriented programming.
+fr: > Elles ont beaucoup de similarités avec les interfaces de Java.
+en: > A Haskell class has a lot of similarities with Java interfaces.
+fr: > Un meilleur mot aurait été `typeClass`, ce qui signifierait un ensemble de types.
+en: > A better word would have been `typeclass`, since that means a set of types.
+fr: > Pour qu'un type appartienne à une classe, toutes les fonctions de cette classe doivent être fournies pour ce type.
+en: > For a type to belong to a class, all functions of the class must be provided for this type.
+fr: > - Dans cet exemple particulier de classe de type, le type `m` doit être un type qui prend un argument.
+en: > - In this particular example of type class, the type `m` must be a type that takes an argument.
+fr: > par exemple `IO a`, mais aussi `Maybe a`, `[a]`, etc...
+en: > for example `IO a`, but also `Maybe a`, `[a]`, etc...
+fr: > - Pour être une monade utile, votre fonction doit obéir à quelques règles.
+en: > - To be a useful monad, your function must obey some rules.
+fr: > Si votre construction n'obéit pas à ces règles, des choses étranges pourraient se produire :
+en: > If your construction does not obey these rules strange things might happens:
+
+ > ~~~
+ > return a >>= k == k a
+ > m >>= return == m
+ > m >>= (\x -> k x >>= h) == (m >>= k) >>= h
+ > ~~~
+
+fr:
Maybe est une monade
+en:
Maybe is a monad
+
+fr: Il y a beaucoup de types différents qui sont des instances de `Monad`.
+en: There are a lot of different types that are instances of `Monad`.
+fr: L'un des plus faciles à décrire est `Maybe`.
+en: One of the easiest to describe is `Maybe`.
+fr: Si vous avez une séquence de valeurs `Maybe`, vous pouvez utiliser les monades pour les manipuler.
+en: If you have a sequence of `Maybe` values, you can use monads to manipulate them.
+fr: C'est particulièrement utile pour enlever des constructions `if..then..else..` trop nombreuses.
+en: It is particularly useful to remove very deep `if..then..else..` constructions.
+
+fr: Imaginez une opération bancaire complexe. Vous êtes éligible pour gagner 700€ seulement si
+en: Imagine a complex bank operation. You are eligible to gain about 700€ only
+fr: vous pouvez effectuer une liste d'opérations sans tomber en dessous de zéro.
+en: if you can afford to follow a list of operations without your balance dipping below zero.
+
+> 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
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs
new file mode 100644
index 0000000..40efd31
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/11_Monads.lhs
@@ -0,0 +1,23 @@
+fr: Maintenant, améliorons cela en utilisant le fait que `Maybe` est une Monade.
+en: Now, let's make it better using Maybe and the fact that it is a Monad
+
+> 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
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs
new file mode 100644
index 0000000..f0871c5
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/12_Monads.lhs
@@ -0,0 +1,66 @@
+fr: Pas mauvais, mais nous pouvons faire encore mieux :
+en: Not bad, but we can make it even better:
+
+> 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
+
+fr: Nous avons prouvé que les monades sont un bon moyen de rendre notre code plus élégant.
+en: We have proven that Monads are a good way to make our code more elegant.
+fr: Remarquez que cette idée d'organisation de code, en particulier pour `Maybe`, peut être utilisée
+en: Note this idea of code organization, in particular for `Maybe` can be used
+fr: dans la plupart des langages impératifs.
+en: in most imperative languages.
+fr: En fait, c'est le type de construction que nous faisons naturellement.
+en: In fact, this is the kind of construction we make naturally.
+
+fr: > Une remarque importante :
+en: > An important remark:
+ >
+fr: > Le premier élement de la séquence qui sera évalué comme `Nothing` stoppera
+en: > The first element in the sequence being evaluated to `Nothing` will stop
+fr: > l'évaluation.
+en: > the complete evaluation.
+fr: > Cela signifie que vous n'exécutez pas toutes les lignes.
+en: > This means you don't execute all lines.
+fr: > Cela découle du caractère paresseux de Haskell.
+en: > You get this for free, thanks to laziness.
+
+fr: Vous pourriez aussi revoir ces exemples avec la définition de `(>>=)` pour `Maybe`
+en: You could also replay these example with the definition of `(>>=)` for `Maybe`
+fr: en tête :
+en: in mind:
+
+
+instance Monad Maybe where
+ (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
+ Nothing >>= _ = Nothing
+ (Just x) >>= f = f x
+
+ return x = Just x
+
+
+
+fr: La monade `Maybe` a prouvé par un simple exemple qu'elle est utile.
+en: The `Maybe` monad proved to be useful while being a very simple example.
+fr: Nous avons vu l'utilité de la monade `IO`.
+en: We saw the utility of the `IO` monad.
+fr: Mais maintenant, voici un exemple encore plus cool : les listes.
+en: But now for a cooler example, lists.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs
new file mode 100644
index 0000000..e32e638
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/03_Hell/02_Monads/13_Monads.lhs
@@ -0,0 +1,67 @@
+fr:
La monade List
+en:
The list monad
+
+blogimage("golconde.jpg","Golconde de Magritte")
+
+fr: La monade `List` nous aide à simuler des calculs non-déterministes.
+en: The list monad helps us to simulate non-deterministic computations.
+fr: C'est parti :
+en: Here we go:
+
+> 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
+
+
+fr: Ma. GIQUE. :
+en: MA. GIC. :
+
+~~~
+[(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)]
+~~~
+
+fr: Pour la monade `List`, il y a aussi un sucre syntaxique :
+en: For the list monad, there is also this syntactic sugar:
+
+> print $ [ (x,y,z) | x <- allCases,
+> y <- allCases,
+> z <- allCases,
+> 4*x + 2*y < z ]
+
+fr: Je ne listerai pas toutes les monades, car il y en a beaucoup.
+en: I won't list all the monads, since there are many of them.
+fr: Utiliser les monades simplifie la manipulations de plusieurs notions dans les langages purs.
+en: Using monads simplifies the manipulation of several notions in pure languages.
+fr: Les monades sont très utiles, en particulier pour :
+en: In particular, monads are very useful for:
+
+fr: - L'E/S (IO),
+en: - IO,
+fr: - les calculs non-déterministes,
+en: - non-deterministic computation,
+fr: - générer des nombres pseudo-aléatoires,
+en: - generating pseudo random numbers,
+fr: - garder un état de configuration,
+en: - keeping configuration state,
+fr: - écrire un état,
+en: - writing state,
+- ...
+
+fr: Si vous m'avez suivi jusqu'ici, alors vous avez terminé !
+en: If you have followed me until here, then you've done it!
+fr: Vous connaissez les monades[^03021301] !
+en: You know monads[^03021301]!
+
+fr: [^03021301]: Vous aurez quand même besoin de pratiquer un peu pour vous habituer à elles et pour comprendre quand les utiliser et créer les vôtres. Mais vous avez déjà fait un grand pas dans cette direction.
+en: [^03021301]: 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/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs
new file mode 100644
index 0000000..61f2030
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/00_appendix.lhs
@@ -0,0 +1,7 @@
+fr:
Appendice
+en:
Appendix
+
+fr: Cette section n'est pas vraiment sur l'apprentissage d'Haskell.
+en: This section is not so much about learning Haskell.
+fr: Elle est ici pour discuter de quelques détails.
+en: It is just here to discuss some details further.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
new file mode 100644
index 0000000..7d1f01f
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs
@@ -0,0 +1,160 @@
+fr:
Revenons sur les arbres infinis
+en:
More on Infinite Tree
+
+fr: Dans la section sur [les structures infinies](#infinite-structures) nous avons vu quelques
+en: In the section [Infinite Structures](#infinite-structures) we saw some simple
+fr: constructions simples.
+en: constructions.
+fr: Malheureusement, nous avons enlevé deux propriétés de notre arbre:
+en: Unfortunately we removed two properties from our tree:
+
+fr: 1. Pas de valeurs identiques
+en: 1. no duplicate node value
+fr: 2. Arbre bien ordonné
+en: 2. well ordered tree
+
+fr: Dans cette section nous allons tenter de garder la première propriété.
+en: In this section we will try to keep the first property.
+fr: Concernant la seconde, nous ne devons pas nous en préoccuper ici mais nous discuterons
+en: Concerning the second one, we must relax it but we'll discuss how to
+fr: de comment la garder le plus possible.
+en: keep it as much as possible.
+
+
+
+This code is mostly the same as the one in the [tree section](#trees).
+
+> 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"
+>
+
+
+
+fr: Notre première étape est de créer une liste de nombres pseudo-aléatoires:
+en: Our first step is to create some pseudo-random number list:
+
+> shuffle = map (\x -> (x*3123) `mod` 4331) [1..]
+
+fr: Pour mémoire, voici la définition de `treeFromList`
+en: Just as a reminder, here is the definition of `treeFromList`
+
+> treeFromList :: (Ord a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x (treeFromList (filter ( (treeFromList (filter (>x) xs))
+
+fr: et
+en: and
+`treeTakeDepth`:
+
+> 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
+
+fr: Voyez le résultats de:
+en: See the result of:
+
+> main = do
+> putStrLn "take 10 shuffle"
+> print $ take 10 shuffle
+> putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)"
+> print $ treeTakeDepth 4 (treeFromList shuffle)
+
+~~~
+% 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
+~~~
+
+fr: Le code fonctionne!
+en: Yay! It ends!
+fr: Attention cependant, cela marchere seulement si vous avez toujours quelque chose à mettre dans une branche.
+en: Beware though, it will only work if you always have something to put into a branch.
+
+fr: Par exemple
+en: For example
+
+
+treeTakeDepth 4 (treeFromList [1..])
+
+
+fr: tournera en boucle pour toujours.
+en: will loop forever.
+fr: Simplement parce que le code essayera d'accéder à première valeur de `filter (<1) [2..]`.
+en: Simply because it will try to access the head of `filter (<1) [2..]`.
+fr: Mais `filter` n'est pas assez intelligent pour comprendre que le résultat est une liste vide.
+en: But `filter` is not smart enought to understand that the result is the empty list.
+
+fr: Toutefois, cela reste un exemple sympa de ce qu'un programme non-stricit a à offrir.
+en: Nonetheless, it is still a very cool example of what non strict programs have to offer.
+
+fr: Laissé pour exercice au lecteur:
+en: Left as an exercise to the reader:
+
+fr: - Prouver l'existence d'un nombre `n` tel que `treeTakeDepth n (treeFromList shuffle)` provoquera une boucle infinie.
+en: - Prove the existence of a number `n` so that `treeTakeDepth n (treeFromList shuffle)` will enter an infinite loop.
+fr: - Trouver une borne supérieur `n`.
+en: - Find an upper bound for `n`.
+fr: - Prouver qu'il n(y a pas de liste `shuffle` qui termine le programme pour n'importe quelle profondeur.
+en: - Prove there is no `shuffle` list so that, for any depth, the program ends.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
new file mode 100644
index 0000000..13e2b44
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs
@@ -0,0 +1,192 @@
+
+
+
+This code is mostly the same as the preceding one.
+
+> import Debug.Trace (trace)
+> 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"
+>
+> 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
+
+
+
+fr: Pour résoudre ces problèmes nous allons modifier légèrement nos
+en: In order to resolve these problem we will modify slightly our
+fr: fonctions `treeFromList` et `shuffle`.
+en: `treeFromList` and `shuffle` function.
+
+fr: Un premier problème est le manque de nombres différents dans notre immlémentation de `shuffle`.
+en: A first problem, is the lack of infinite different number in our implementation of `shuffle`.
+fr: Nous avons généré seulement `4331` nombres différents.
+en: We generated only `4331` different numbers.
+fr: Pour résoudre cela nous allons faire un meilleure fonction `shuffle`.
+en: To resolve this we make a slightly better `shuffle` function.
+
+> 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
+
+fr: Cette fonction à la propriété de ne pas avoir de bornes supérieure ou inférieure.
+en: This shuffle function has the property (hopefully) not to have an upper nor lower bound.
+fr: Mais avoir une meilleure list `shuffle` n'est pas assez pour entrer dans une boucle infinie.
+en: But having a better shuffle list isn't enough not to enter an infinite loop.
+
+fr: Généralement, nous ne pouvons pas décider que `filter ( Tous les élements de la branche de gauche doit être strictement inférieur au la valeur racine.
+en: > Any element of the left (resp. right) branch must all be strictly inferior (resp. superior) to the label of the root.
+
+fr: Remarquez que cela donnera _souvent_ un arbre ordonné.
+en: Remark it will remains _mostly_ an ordered binary tree.
+fr: En outre, avec cette construction, chaque noeud est unique dans l'arbre.
+en: Furthermore, by construction, each node value is unique in the tree.
+
+fr: Voici notre nouvelle version de `treeFromList`. Nous avons simplement remplacé `filter` par `safefilter`.
+en: Here is our new version of `treeFromList`. We simply have replaced `filter` by `safefilter`.
+
+> treeFromList :: (Ord a, Show a) => [a] -> BinTree a
+> treeFromList [] = Empty
+> treeFromList (x:xs) = Node x left right
+> where
+> left = treeFromList $ safefilter ( right = treeFromList $ safefilter (>x) xs
+
+fr: Cette nouvelle fonction `safefilter` est presque équivalente à `filter` mais n'entre pas dans des boucles infinies si le résultat est une liste finie.
+en: This new function `safefilter` is almost equivalent to `filter` but don't enter infinite loop if the result is a finite list.
+fr: Si elle ne peut pas trouver un élément pour lequel le test est vrai après 10000 étapes consécutives, alors elle considère que la recherche est finie.
+en: 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.
+
+> 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)
+
+fr: Maintenant faites tourner le programme et soyez heureux:
+en: Now run the program and be happy:
+
+> main = do
+> putStrLn "take 10 shuffle"
+> print $ take 10 shuffle
+> putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)"
+> print $ treeTakeDepth 8 (treeFromList $ shuffle)
+
+fr: Vous devriez réaliser que le temps nécessaire pour afficher chaque valeur est différent.
+en: You should realize the time to print each value is different.
+fr: C'est parce que Haskell calcule chaque valeur lorsqu'il en a besoin.
+en: This is because Haskell compute each value when it needs it.
+fr: Et dans ce cas, il est demandé de l'afficher à l'écran.
+en: And in this case, this is when asked to print it on the screen.
+
+fr: Vous pouvez même essayer de remplacer la profondeur de `8` par `100`.
+en: Impressively enough, try to replace the depth from `8` to `100`.
+fr: Cela marchera sans tuer votre RAM!
+en: It will work without killing your RAM!
+fr: La gestion de la mémoire est faite naturellement par Haskell.
+en: The flow and the memory management is done naturally by Haskell.
+
+fr: Laissé comme exercices au lecteur:
+en: Left as an exercise to the reader:
+
+fr: - Même avec une grande valeur constante pour `deep` et `nbTry`, cela semble marcher correctement. Mais dans le pire des cas, cela peut devenir exponentiel.
+en: - Even with large constant value for `deep` and `nbTry`, it seems to work nicely. But in the worst case, it can be exponential.
+fr: Créez la pire liste à donner comme paramètre à `treeFromList`.
+en: Create a worst case list to give as parameter to `treeFromList`.
+fr: _indice_: pensez à (`[0,-1,-1,....,-1,1,-1,...,-1,1,...]`).
+en: _hint_: think about (`[0,-1,-1,....,-1,1,-1,...,-1,1,...]`).
+fr: - J'ai commencé à implémenter `safefilter` comme ceci:
+en: - I first tried to implement `safefilter` as follow:
+
+ safefilter' f l = if filter f (take 10000 l) == []
+ then []
+ else filter f l
+
+fr: Expliquer pourquoi cela ne fonctionne pas et peut entrer dans une boucle infinie.
+en: Explain why it doesn't work and can enter into an infinite loop.
+fr: - Supposez que `shuffle` est une liste de nombre réellement aléatoires avec de plus en plus de bornes.
+en: - Suppose that `shuffle` is real random list with growing bounds.
+fr: Si vous étudiez un peu cette structure, vous découvrirez qu'elle a toutes les chances
+en: If you study a bit this structure, you'll discover that with probability 1,
+fr: d'être finie.
+en: this structure is finite.
+fr: En utilisant le code suivant
+en: Using the following code
+fr: (supposez que nous pouvons utliser `safefilter'` directement comme si cela n'était pas dans le `where` de `safefilter`.
+en: (suppose we could use `safefilter'` directly as if was not in the where of safefilter)
+fr: trouvez une définition de `f` telle que, avec une probabilité de `1`,
+en: find a definition of `f` such that with probability `1`,
+fr: `treeFromList' shuffle` est infinie?. Et prouvez-le.
+en: `treeFromList' shuffle` is infinite. And prove it.
+fr: Avertissement, ce n'est qu'une conjecture.
+en: Disclaimer, this is only a conjecture.
+
+
+treeFromList' [] n = Empty
+treeFromList' (x:xs) n = Node x left right
+ where
+ left = treeFromList' (safefilter' (x) xs (f n)
+ f = ???
+
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs
new file mode 100644
index 0000000..7e11e0c
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/code/04_Appendice/02_Thanks/10_Thanks.lhs
@@ -0,0 +1,15 @@
+en: ## Thanks
+fr: ## Remerciements
+
+fr: Merci à [`/r/haskell`](http://reddit.com/r/haskell) et
+en: Thanks to [`/r/haskell`](http://reddit.com/r/haskell) and
+[`/r/programming`](http://reddit.com/r/programming).
+fr: Vos commentaires étaient plus que bienvenus.
+en: Your comment were most than welcome.
+
+fr: Particulièrement, je voudrais remercier mille fois [Emm](https://github.com/Emm)
+en: Particularly, I want to thank [Emm](https://github.com/Emm) a thousand times
+fr: pour le temps qu'il a consacré à corriger mon anglais.
+en: for the time he spent on correcting my English.
+fr: Merci beaucoup.
+en: Thank you man.
diff --git a/src/Scratch/fr/blog/Haskell-the-Hard-Way/index.html b/src/Scratch/fr/blog/Haskell-the-Hard-Way/index.html
new file mode 100644
index 0000000..c1ca793
--- /dev/null
+++ b/src/Scratch/fr/blog/Haskell-the-Hard-Way/index.html
@@ -0,0 +1,2389 @@
+
+
+
+
+ YBlog - Haskell comme un vrai!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Je pense vraiment que tous les développeurs devraient apprendre Haskell. Peut-être pas devenir des ninjas d’Haskell, mais au moins savoir ce que ce langage a de particulier. Son apprentissage ouvre énormément l’esprit.
+
La plupart des langages partagent les mêmes fondements :
les structures de données, les objets et les classes
+
+
Haskell est très différent. Ce langage utilise des concepts dont je n’avais jamais entendu parler avant. Beaucoup de ces concepts pourront vous aider à devenir un meilleur développeur.
+
Plier son esprit à Haskell peut être difficile. Ce le fut pour moi. Dans cet article, j’essaye de fournir les informations qui m’ont manquées lors de mon apprentissage.
+
Cet article sera certainement difficile à suivre. Mais c’est voulu. Il n’y a pas de raccourci pour apprendre Haskell. C’est difficile. Mais je pense que c’est une bonne chose. C’est entre autres parce qu’Haskell est difficile qu’il est intéressant.
+
La manière conventionnelle d’apprendre Haskell est de lire deux livres. D’abord “Learn You a Haskell” et ensuite “Real World Haskell”. Je pense aussi que c’est la bonne manière de s’y prendre. Mais apprendre même un tout petit peu d’Haskell est presque impossible sans se plonger réellement dans ces livres.
+
Cet article fait un résumé très dense et rapide des aspects majeurs d’Haskell. J’y ai aussi rajouté des informations qui m’ont manqué pendant l’apprentissage de ce langage.
+
Pour les francophones : je suis désolé. Je n’ai pas eu le courage de tout retraduire en français. Sachez cependant que si vous êtes plusieurs à insister, je ferai certainement l’effort de traduire l’article en entier. Et si vous vous sentez d’avoir une bonne âme je ne suis pas contre un peu d’aide. Les sources de cet article sont sur github.
+
Cet article contient cinq parties :
+
+
Introduction : un exemple rapide pour montrer qu’Haskell peut être facile.
+
Les bases d’Haskell : La syntaxe et des notions essentielles
+
Partie difficile :
+
+
Style fonctionnel : un exemple progressif, du style impératif au style fonctionnel ;
+
Types : la syntaxe et un exemple d’arbre binaire ;
+
Structure infinie : manipulons un arbre infini !
+
+
Partie de difficulté infernale :
+
+
Utiliser les IO : un exemple très minimal ;
+
Le truc des IO révélé : les détails cachés d’IO qui m’ont manqués
+
Les monades : incroyable à quel point on peut généraliser
+
+
Appendice :
+
+
Revenons sur les arbres infinis : une discussion plus mathématique sur la manipulation d’arbres infinis.
+
+
+
+Note: Chaque fois que vous voyez un séparateur avec un nom de fichier se terminant par lhs, vous pouvez cliquer sur le nom de fichier et télécharger le fichier. Si vous sauvegardez le fichier sour le nom filename.lhs, vous pouvez l’exécuter avec :
+
+runhaskell filename.lhs
+
+
Certains ne marcheront pas, mais la majorité vous donneront un résultat. Vous devriez voir un lien juste en dessous.
runhaskell: Exécuter un programme sans le compiler. Pratique mais très lent comparé aux programmes compilés.
+
+
+Ne soyez pas effrayés!
+
+
+
+
+
Beaucoup de livres/articles sur Haskell commencent par présenter des formules ésotériques (Algorithmes de tri rapide, suite de Fibonacci, etc…). Je ferai l’exact opposé En premier lieu je ne vous montrerai pas les super-pouvoirs d’Haskell. Je vais commencer par les similarités avec les autres langages de programmation. Commençons par l’indispensable “Hello World!”.
Vous pouvez également télécharger la source Haskell littérale. Vous devriez voir un lien juste au dessus du titre de l’introduction. Téléchargez ce fichier en tant que 00_hello_world.lhs et:
Avant de continuer, vous devez êtres avertis à propos de propriétés essentielles de Haskell.
+
Fonctionnel
+
Haskell est un langage fonctionnel Si vous avez déjà travaillé avec un langage impératif, vous devrez apprendre beaucoup de nouvelles choses. Heureusement beaucoup de ces nouveaux concepts vous aidera à programmer même dans un langage impératif.
+
Typage Statique Intelligent
+
Au lieu de bloquer votre chemin comme en C, C++ ou Java, le système de typage est ici pour vous aider.
+
Pureté
+
Généralement vos fonctions ne modifieront rien du le monde extérieur. Cela veut dire qu’elles ne peuvent pas modifier la valeur d’une variable, lire du texte entré par un utilisateur, écrire sur l’écran, lancer un missile. D’un autre coté, avoir un code parallèle devient très facile. Haskell rend très clair où les effets apparaissent et où le code est pur. De plus, il devient beaucoup plus aisé de raisonner sur son programme. La majorité des bugs seront évités dans les parties pures de votre programme.
+
En outre, les fonctions pures suivent une loi fondamentale en Haskell:
+
+
Appliquer une fonction avec les mêmes paramètres retourne toujours la même valeur.
+
+
Paresse
+
La paresse par défaut est un choix de conception de langage très rare. Par défaut, Haskell évalue quelque chose seulement lorsque cela est nécessaire. En conséquence, cela fournit un moyen très élégant de manipuler des structures infinies, par exemple.
+
Un dernier avertissement sur comment vous devriez lire le code Haskell. Pour moi, c’est comme lire des papiers scientifiques. Quelques parties sont très claires, mais quand vous voyez une formule, concentrez-vous dessus et lisez plus lentement. De plus, lorsque vous apprenez Haskell, cela n’importe vraiment pas si vous ne comprenez pas les détails syntaxiques. Si vous voyez un >>=, <$>, <- ou n’importe quel symbole bizarre, ignorez-les et suivez le déroulement du code.
+
+Déclaration de fonctions
+
+
Vous avez déjà dû déclarer des fonctions comme cela:
N’oubliez pas, Haskell utilise beaucoup les fonctions et les types. C’est très facile de les définir. La syntaxe a été particulièrement réfléchie pour ces objets.
+
+Un exemple de type
+
+
Même si ce n’est pas obligatoire, les informations de type pour les fonctions sont habituellement déclarées explicitement. Ce n’est pas indispensable car le compilateur est suffisamment intelligent pour le déduire à votre place. Cependant, c’est une bonne idée car cela montre bien l’intention du développeur et facilite la compréhension.
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)
+
Le problème est que 4.2 n’est pas de type Int (NDT: Il n’est pas un entier)
Maintenant, ça marche! Heureursement, nous n’avons pas à déclarer un nouvelle fonction pour chaque type différent. Par exemple, en C, vous auriez dû déclarer un fonction pour int, pour float, pour long, pour double, etc…
+
Mais quel type devons nous déclarer? Pour découvrir le type que Haskell a trouvé pour nous, lançons ghci:
+
+% 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
+
+
Hein? Quel ce type étrange?
+
Num a => a -> a -> a
+
Premièrement, concentrons-nous sur la partie de droite: a -> a -> a. Pour le comprendre, regardez cette liste d’exemples progressifs:
+
+
+
+
+
+
+
+
Le type écrit
+
Son sens
+
+
+
+
+
Int
+
Le type Int
+
+
+
Int -> Int
+
Le type de la fonction qui prend un Int et retourne un Int
+
+
+
Float -> Int
+
Le type de la fonction qui prend un Float et retourne un Int
+
+
+
a -> Int
+
Le type de la fonction qui prend n’importe quel type de variable et retourne un Int
+
+
+
a -> a
+
Le type de la fonction qui prend n’importe quel type a et retourne une variable du même type a
+
+
+
a -> a -> a
+
Le type de la fonction qui prend deux arguments de n’importe quel typea et retourne une variable de type a
+
+
+
+
Dans le type a -> a -> a, la lettre a est une variable de type. Cela signifie que f est une fonction avec deux arguments et que les deux arguments et le résultat ont le même type. La variable de type a peut prendre de nombreuses valeurs différentes Par exemple Int, Integer, Float…
+
Donc à la place d’avoir un type forcé comme en C et de devoir déclarer une fonction pour int, long, float, double, etc., nous déclarons une seule fonction comme dans un langage typé de façon dynamique.
+
C’est parfois appelé le polymorphisme paramétrique. C’est aussi appelé avoir un gâteau et le manger.
+
Généralement a peut être de n’importe quel type, par exemple un String ou un Int, mais aussi des types plus complexes comme Trees, d’autres fonctions, etc. Mais ici notre type est préfixé par Num a =>.
+
Num est une classe de type. Une classe de type peut être comprise comme un ensemble de types Num contient seulement les types qui se comportent comme des nombres. Plus précisement, Num est une classe qui contient des types qui implémentent une liste spécifique de fonctions, en particulier (+) et (*).
+
Les classes de types sont une structure de langage très puissante. Nous pouvons faire des trucs incroyablement puissants avec. Nous verrons cela plus tard.
+
Finalement, Num a => a -> a -> a signifie:
+
soit a un type qui appartient à la classe Num. C’est une fonction qui prend une variable de type a et retourne une fonction de type (a -> a)
+
Oui, c’est étrange. En fait, en Haskell aucune fonction ne prend réellement deux arguments. Au lieu de cela toutes les fonctions n’ont qu’un argument unique. Mais nous retiendrons que prendre deux arguments est équivalent à n’en prendre qu’un et à retourner une fonction qui prend le second argument en paramètre.
+
Plus précisement f 3 4 est équivalent à (f 3) 4. Remarque: f 3 est une fonction:
+
f :: Num a => a -> a -> a
+
+g :: Num a => a -> a
+g = f 3
+
+g y ⇔ 3*3 + y*y
+
Une autre notation existe pour les fonctions. La notation lambda nous autorise à créer des fonctions sans leur assigner un nom. On les appelle des fonctions anonymes. nous aurions donc pu écrire:
+
g = \y -> 3*3 + y*y
+
Le \ esst utilisé car il ressemble à un λ et est un caractère ASCII.
+
Si vous n’êtes pas habitué à la programmation fonctionnelle, votre cerveau devrait commencer à chauffer Il est temps de faire une vraie application.
Cela fonctionne, car 3 est une représentation valide autant pour les nombres fractionnaires comme Float que pour les entiers. Comme 2.4 est un nombre fractionnaire, 3 est interprété comme une autre nombre fractionnaire
Le compilateur se plaint. Les deux paramètres doivent avoir le même type.
+
Si vous pensez que c’est une mauvaise idée et que le compilateur devrait faire la transformation depuis un type à un autre pour vous, vous devriez vraiment regarder cette vidéo géniale (et amusante): WAT (NDT: En Anglais)
Je vous suggère de seulement survoler cette partie Pensez-y seulement comme à une référence. Haskell a beaucoup de caractèristiques Il manque beaucoup d’informations ici. Revenz ici si la notation vous semble étrange.
+
J’utilise le symbole ⇔ pour signifier que deux expressions sont équivalentes. C’est une notation extérieure, ⇔ n’existe pas en Haskell. Je vais aussi utiliser le symoble ⇒ quelle est la valeur que retourne une fonction.
[] ⇔ liste vide
+[1,2,3] ⇔ Liste d'entiers
+["foo","bar","baz"] ⇔ Liste de chaînes de caractères
+1:[2,3] ⇔ [1,2,3], (:) ajoute un élément au début
+1:2:[] ⇔ [1,2]
+[1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concaténation de deux listes
+[1,2,3] ++ ["foo"] ⇔ ERREUR String ≠ Integral
+[1..4] ⇔ [1,2,3,4]
+[1,3..10] ⇔ [1,3,5,7,9]
+[2,3,5,7,11..100] ⇔ ERREUR! Je ne suis pas si intelligent!
+[10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1]
+
+Chaînes de caractères
+
+
En Haskell les chaînes de caractères sont des listes de Char.
Remarque: Dans un vrai code vous n’utiliserez pas des listes de char pour représenter du texte. Vous utiliserez plus souvent Data.Text à la place. Si vous voulez représenter un chapelet de caractères ASCII, vous utiliserez Data.ByteString.
+
+
+Tuples
+
+
Le type d’un couple est (a,b). Les éléments d’un tuple peuvent avoir des types différents.
+
-- tous ces tuples sont valides
+(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
+
+Traiter avec les parenthèses
+
+
Pour enlever des parenthèses vous pouvez utiliser deux fonctions: ($) et (.).
+
-- Par défaut:
+f g h x ⇔ (((f g) h) x)
+
+-- le $ remplace les parenthèses depuis le $
+-- jusqu'à la fin de l'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))
+
+-- (.) permet de faire des compositions de fonctions
+(f . g) x ⇔ f (g x)
+(f . g . h) x ⇔ f (g (h x))
x :: Int ⇔ x est de type Int
+x :: a ⇔ x peut être de n'importe quel type
+x :: Num a => a ⇔ x peut être de n'importe quel type a
+ tant qu' a appartient à la classe de type Num
+f :: a -> b ⇔ f est une fonction qui prend un a et retourne un b
+f :: a -> b -> c ⇔ f est une fonction qui prend un a et retourne un (b→c)
+f :: (a -> b) -> c ⇔ f est une fonction qui prend un (a→b) et retourne un c
+
Rappelez-vous que définir le type d’une fonction avant sa déclaration n’est pas obligatoire. Haskell infère le type le plus général pour vous. Mais c’est considéré comme une bonne pratique.
Remarquez que ^ utilise une notation infixée. Pour chaque opérateur infixe il y a une notation préfixée associée. Vous devez juste l’écrire entre parenthèses.
Dans cette section, je vais vous donner un court exemple de l’impressionante capacité de remaniement de Haskell. Nous allons sélectionner un problème et le résoudre à la manière d’un langage impératif standard. Ensuite, je ferais évoluer le code. Le résultat final sera plus élégant et plus facile à adapter.
+
résolvons les problèmes suivants:
+
+
Soit une liste d’entiers, retourner la somme des nombres pairs de cette liste.
+
exemple: [1,2,3,4,5] ⇒ 2 + 4 ⇒ 6
+
+
Pour montrer les différences entre les approches fonctionnelle et impérative, je vais commencer par donner la solution impérative (en JavaScript):
En Haskell, en revanche, nous n’avons pas de variables ou un boucle for. Une des solutions pour parvenir au même résultat sans boucles est d’utiliser la récursion.
+
+
Remarque: La récursion est souvent perçue comme lente dans les langages impératifs. Mais ce n’est généralement pas le cas en programmation fonctionnelle. La plupart du temps Haskell gérera les fonctions récursives efficacement.
+
+
Voici la version C de la fonction récursive. Remarquez que je suppose que la liste d’int fini avec la première valeur 0.
+*Main> evenSum [1..5]
+accumSum 0 [1,2,3,4,5]
+1 est impair
+accumSum 0 [2,3,4,5]
+2 est pair
+accumSum (0+2) [3,4,5]
+3 est impair
+accumSum (0+2) [4,5]
+4 est pair
+accumSum (0+2+4) [5]
+5 est impair
+accumSum (0+2+4) []
+l == []
+0+2+4
+0+6
+6
+
+
En venant d’un langage impératif, tout devrait vous sembler juste. En fait, beaucoup de choses peuvent être améliorées ici. Tout d’abord, nous pouvons généraliser le type.
Mais le pattern matching peut aller encore plus loin. Il est également capable d’inspecter les données internes d’un valeur complexe. Nous pouvons ainsi remplacer
Pour rendre les choses plus faciles, nous devrions utiliser des fonctions d’ordre supérieur. Ce sont des fonctions qui prennent des fonctions en paramètres
La fonction filter prend une fonction du type (a -> Bool) et une liste de type [a]. Elle retourne une liste qui contient seulement les élements pour qui la fonction a retourné True.
+
La prochaine étape est d’utiliser une autre technique pour accomplir la même chose qu’une boucle. Nous allons utiliser la fonction foldl pour accumuler une valeur au fur et à mesure que l’on parcoure la liste. La fonction foldl capture un modèle de code général:
Mais comme Haskell est paresseux, il n’évalue pas (f z x) et le met simplement dans la pile. C’est pourquoi on utilise généralement foldl', une version stricte de foldl, Si vous ne comprenez pas encore ce que paresseux ou strict signifie, ne vous inquiétez pas, suivez le code comme si foldl' et foldl étaient identiques
Il est temps de discuter de la direction qu’a pris notre code depuis que nous avons introduit plus d’idiomes fonctionnels. Que gagnons-nous à utiliser des fonctions d’ordre supérieur?
+
D’abord, vous pourriez penser que la principale différence est la brièveté. Mais en réalité, il s’agit d’une meilleure façon de penser. Supposons que nous voulons modifier légèrement notre fonction, par exemple, pour qu’elle renvoie la somme de tous les carrés pairs des éléments de la liste.
Nous avons juste eu à ajouter une autre “fonction de transformation”.
+
map (^2) [1,2,3,4] ⇔ [1,4,9,16]
+
La fonction map applique simplementune fonction à tous les élements d’une liste.
+
Nous n’avons rien modifié à l’intérieur de notre définition de fonction. Cela rend le code plus modulaire. En plus de cela, vous pouvez penser à votre fonction plus mathématiquement. Vous pouvez aussi utilier votre fonction avec d’autres, au besoin: vous pouvez utiliser compose, map, fold ou filter sur notre nouvelle fonction.
+
Modifier la version 1 est laissé comme un exercice pour le lecteur ☺.
+
Si vous croyez avoir atteint le bout de la généralisation, vous avez tout faux. Par example, il y a un moyen d’utiliser cette fonction non seulement sur les listes mais aussi sur n’importe quel type récursif. Si vous voulez savoir comment, je vous suggère de lire cet article: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, Fokkinga and Paterson (NDT: en anglais, mais là vous vous en seriez douté je pense ☺)
+
Cet exemple montre à quel point la programmation fonctionnelle pure est géniale. Malheureusement, utiliser cet outil n’est pas adapté à tous les besoins. Ou alors un langage qui le premettrait n’a pas encore été trouvé.
+
Une des grands pouvoirs de Haskell est sa capacité à créer des DSLs (Domain Specific Language, en français : langage spécifique à un domaine) Il est ainsi facile de changer le pardigme de programmation
+
En fait, Haskell peut très bien vous permettre d’écrire des programmes impératifs. Comprendre cela a été très difficile pour moi lorsque j’apprenais Haskell. Beaucoup d’efforts tendent à expliquer la supériorité de l’approche fonctionnele. Puis lorsque vous commencez à utliser le style impératif en Haskell, Il peut être difficile de comprendre quand et où l’utliser.
+
Mais avant de parler de ce super-pouvoir de Haskell, nous devons parler d’un autre aspet essentiel: les Types.
type Name = AnotherType n’est qu’un alias de type, le compilateur ne fera pas la différence entre les deux.
+
data Name = NameConstructor AnotherType le compilateur fera la différence.
+
data permet de construire de nouvelles structures qui peuvent être récursives.
+
deriving est magique et créé automatiquement des fonctions pour vous.
+
+
+
En Haskell, les types sont forts et statiques.
+
Pourquoi est-ce important? Cela vous aidera a éviter beaucoup d’erreurs. En Haskell, la majorité des bugs est repérée durant la compilation de votre programme. Et la raison principale de cela est l’inférence de type durant la compilation. L’inférence de type permet de détecter plus facilement lorsque vous utilisez le mauvais paramètre au mauvais endroit, par exemple.
+
+Inférence de type
+
+
Le typage statique est généralement essentiel pour une exécution rapide. Mais la plupart des langages typés statiquement ont du mal à généraliser des concepts. La “grâce salvatrice” de Haskell est qu’il peut inférer des types.
+
Voici un exemple simple, la fonction square en Haskell:
Cette fonction peut mettre au carré n’importe quel type Numeral. Vous pouvez l’utilser avec un Int, un Integer, un Float, un Fractional ou même un Complex. Preuve par l’exemple:
+
% ghci
+GHCi, version 7.0.4:
+...
+Prelude> let square x = x*x
+Prelude> square 2
+4
+Prelude> square 2.1
+4.41
+Prelude> -- charge le module Data.Complex
+Prelude> :m Data.Complex
+Prelude Data.Complex> square (2 :+ 1)
+3.0 :+ 4.0
+
x :+ y est la notation pour le complexe (x + iy)
+
Comparons maintenant avec la quantité de code nécessaire pour le faire en C:
Pour chaque type, vous avez besoin d’écrire une nouvelle fonction. Le seul moyen de se débarrasser de ce problème est d’utiliser des astuces de méta-programmation, par exemple en utilisant le pré-processeur. en C++ il y a un meilleur moyen, les templates:
C++ fait un bien meilleur travail que C ici. Mais pour des fonctions plus complexes, la syntaxe sera difficile à suivre. Voyez cet article pour quelques exemples. (_NDT: toujours en anglais)
+
En C++ vous devez déclarer qu’une fonction peut marcher avec différents types. En Haskell, c’est le contraire. La fonction sera aussi générale que possible par défaut.
+
L’inférence de type donne à Haskell le sentiment de liberté que les langages dynamiquement typés proposent. Mais contrairement aux langages dynamiquement typés, la majorité des erreurs est détectée avant de lancer le programme. Généralement, en Haskell:
+
+
“Si ça compile, ça fait certainement ce que vous attendiez.”
Le code sera compilé et exécuté. En fait vous pouvez remplace Name, Color et String n’importe où. Le compilateur les traitera comme si ils était complétement identiques.
+
Une autre méthode est de créer vos propres type avec le mot-clé data.
Maintenant, si vous échangez les paramètres de showInfos, le compilateur se plaint! Au seul prix d’être plus verbeux, vous écartez définitivement cette erreur potentielle.
+
Remarquez aussi que les constructeurs sont des fonctions :
Si vous voulez pouvoir écrire (Show), lire (Read), tester l’égalité (Eq) et comparer (Ord) votre nouvelle structure, vous pouvez demander à Haskell de dériver les fonctions appropriées pour vous.
Quand vous ajoutez deriving (Show) à votre déclaration, Haskell crée une fonction show pour vous. Nous verrons bientôt comment utiliser sa propre fonction show.
Juste pour le plaisir, codons un meilleur affichage pour nos arbres. Je me suis simplement amusé à faire une belle fonction pour afficher les arbres de façon générale. Vous pouvez passer cette partie si vous la trouvez difficile à suivre.
+
Nous avons quelques changements à faire. Enlevons le deriving (Show) de la déclaration de notre type BinTree. Il serait aussi utile de faire de BinTree une instance de (Eq et Ord), nous serons ainsi capable de tester l’égalité et de comparer des arbres.
Sans le deriving (Show), Haskell ne crée pas de méthode show pour nous. Nous allons créer notre propre version. Pour accomplir cela, nous devons déclarer que notre type BinTree a est une instance de la classe de type Show. La syntaxe générale est :
Voici ma version pour afficher un arbre binaire. Ne vous inquiétez pas de sa complexité apparente. J’ai fait beaucoup d’améliorations pour afficher même les objets les plus étranges.
Maintenant c’est beaucoup mieux ! La racine est montrée en commençant la ligne avec le caractère <. Et chaque ligne suivante est commence par :. Mais nous pourrions aussi utiliser un autre type.
Remarquez que les arbres en double ne sont pas insérés. Il n’y a qu’un seul arbre correspondant à "I","HEARD". Nous avons ceci presque gratuitement, car nous avons déclaré Tree comme instance de Eq.
+
Voyez à quel point cette structure est formidable : Nous pouvons faire des arbres contenant seulement des entiers, des chaînes de caractères, mais aussi d’autres arbres. Et nous pouvons même faire un arbre contenant un arbre d’arbres!
En fait, si vous êtes un petit peu pédant, vous devriez dire que Haskell est non-strict (NDT: En anglais, pour changer). La paresse est juste une implémentation commune aux langages non-stricts.
+
Alors que signifie “non-strict”? D’après le wiki de Haskell :
+
+
La réduction (terme mathématique pour “évaluation”) procède depuis l’extérieur.
+
Donc si vous avez (a+(b*c)), alors vous réduisez + d’abord, puis vous réduisez (b*c)
Un autre moyen de créer cet arbre est d’utiliser une fonction d’ordre supérieur. Cette fonction devrait être similaire à map n, mais devrait travailler sur un BinTree au lieu d’une liste. Voici cette fonction :
NB: Je ne parlerai pas plus de cette fonction ici. Si vous vous intéressez à la généralisation de map à d’autres structures de données, cherchez des informations sur les foncteurs et fmap.
Félicitations pour être allé si loin! Maitenant, les choses vraiment extrêmes peuvent commencer.
+
Si vous êtes comme moi, vous êtes déjà familier avec le style fonctionnel. Vous devriez également comprendre les avantages de la paresse par défaut. Mais vous ne comprenez peut-être pas vraiment par où commencer pour faire un vrai programme. Et en particulier :
+
+
Comment s’occuper des effets ?
+
Pourquoi y a t-il une étrange notation impérative lorsque l’on s’occupe de l’Entrée/Sortie? (E/S, IO pour Input/Output en anglais)
+
+
Accrochez-vous, les réponses risquent d’être compliquées. Mais elles en valent la peine.
Une fonction typique qui fait de l’IO ressemble à un programme impératif:
+
f :: IO a
+f = do
+ x <- action1
+ action2 x
+ y <- action3
+ action4 x y
+
+
Pour définir la valeur d’un objet on utilise <- .
+
Le type de chaque ligne est IO *; dans cet exemple:
+
+
action1 :: IO b
+
action2 x :: IO ()
+
action3 :: IO c
+
action4 x y :: IO a
+
x :: b, y :: c
+
+
Quelques objets ont le type IO a, cela devrait vous aider à choisir. En particulier vous ne pouvez pas utiliser de fonctions pures directement ici. Pour utiliser des fonctions pures vous pourriez faire action2 (pureFunction x) par exemple.
+
+
+
Dans cette section, je vais expliquer comment utiliser l’IO, pas comment ça marche. Vous verrez comment Haskell sépare les parties pures et impures du programme.
+
Ne vous arrêtez pas sur les détails de la syntaxe Les réponses viendront dans la section suivante.
+
Que cherchons-nous à faire?
+
+
Demander une liste de nombres à l’utilisateur. Afficher la somme de ces nombres.
Nous devrions aussi prêter attention à l’effet du symbole <-.
+
do
+ x <- something
+
Si something :: IO a alors x :: a.
+
Une autre remarque importante sur l’IO: Toutes les lignes d’un bloc do doivent être d’une des deux formes suivantes :
+
action1 :: IO a
+ -- in this case, generally a = ()
+
ou
+
value <- action2 -- where
+ -- action2 :: IO b
+ -- value :: b
+
Ces deux types de ligne correspondent à deux types différents de séquençage d’action. La signification de cette phrase devrait être plus claire à la fin de la prochaine section.
Maintenant voyons comment ce programme se comporte. Par exemple, que ce passe-t-il si l’utilisateur entre une mauvaise valeur? Essayons :
+
% runghc 02_progressive_io_example.lhs
+ Enter a list of numbers (separated by comma):
+ foo
+ Prelude.read: no parse
+
Argh! Un message d’erreur effrayant et un crash ! Notre première amélioration sera de répondre avec un message plus amical.
+
Pour faire cela, nous devons détecter que quelque chose s’est mal passé. Voici un moyen de le faire : utiliser le type Maybe. C’est un type très utilisé en Haskell.
C’est un bon moyen de dire qu’il y a eu une erreur en essayant de créer/évaluer une valeur. La fonction maybeRead en est un bon exemple. C’est une fonction similaire à read3, mais s’il y a un problème, la valeur retournée est Nothing. Si la valeur est bonne, la valeur retournée est Just <la valeur>. Ne vous efforcez pas trop de comprendre cette fonction. J’utilise une fonction de plus bas niveau que read : reads.
Maintenant, pour être plus lisible, on définit une fonction comme ceci : Si la chaîne a un mauvais format, elle retournera Nothing. Sinon, par exemple pour “1,2,3”, cela retournera Just [1,2,3].
Notez que le type de chaque expression dans le bloc do de main reste de la forme IO a. La seule construction étrange est error. Disons juste que error msg prend le type nécessaire (ici, IO ()).
+
Une chose très importante à noter est le type de toutes les fonctions définies jusqu’ici. Il n’y a qu’une seule fonction qui contient IO dans son type : main. Cela signifie que main est impure. Mais main utilise getListFromString, qui, elle, est pure. Nous pouvons donc facilement repérer quelles fonctions sont pures et lesquelles sont impures, seulement en regardant leur type.
+
Pourquoi la pureté a-t-elle de l’importance? Parmi ses nombreux avantages, en voici trois :
+
+
Il est beaucoup plus facile de penser à du code pur qu’à du code impur.
+
La pureté vous protège de tous les bugs difficiles à reproduire dûs aux effets de bord.
+
Vous pouvez évaluer des fonctions pures dans n’importe quel ordre ou en parallèle, sans prendre de risques.
+
+
C’est pourquoi vous devriez mettre le plus de code possible dans des fonctions pures.
Cette fonction est de type IO [Integer]. Cela signifie que la valeur récupérée est de type [Integer] et est le résultat d’actions d’E/S. D’aucuns diront avec enthousiasme :
+
+
«C’est un [Integer] dans un IO !»
+
+
Si vous voulez comprendre les détails derrière tout cela, vous devrez lire la prochaine section. Mais si vous voulez seulement utiliser l’E/S, contentez-vous pratiquer un peu et rappelez-vous de penser aux types.
+
Finalement, notre fonction mainest bien plus simple :
Nous avons fini notre introduction à l’IO. C’était plutôt rapide. Voici les principales choses à retenir :
+
+
Dans le bloc do, chaque expression doit avoir le type IO a. Vous êtes donc limité quant au panel d’expression disponibles. Par exemple, getLine, print, putStrLn, etc…
+
Essayez d’externaliser le plus possible les fonctions pures.
+
le type IO a signifie : une action d’E/S qui retourne un élément de type a. L’IO représente des actions; sous le capot, IO a est le type d’une fonction. Lisez la prochaine section si vous êtes curieux.
+
+
Si vous pratiquez un peu, vous devriez être capable d’utiliser l’IO.
+
+
-Exercices_:
+
+
Écrivez un programme qui additionne tous ses arguments. Utilisez la fonction getArgs.
Première remarque : on dirait de l’impératif. Haskell est assez puissant pour faire sembler impératif du code impur. Par exemple, si vous le vouliez vous pourriez créer une boucle while en Haskell. En fait, pour utiliser les IO, le style impératif est en général plus approprié.
+
Mais vous devriez avoir remarqué que la notation est inhabituelle. Voici pourquoi, en détail.
+
Dans un langage impur, l’état du monde peut être vu comme une énorme variable globale cachée. Cette variable cachée est accessible par toutes les fonctions du langage. Par exemple, vous pouvez lire et écrire dans un fichier avec n’importe quelle fonction. Le fait que le fichier putatif existe ou non est une éventualité qui relève des états possibles que le monde courant peut prendre.
+
En Haskell l’état courant du monde n’est pas caché. Au contraire, il est dit explicitement que main est une fonction qui change potentiellement l’état du monde. Son type est donc quelque chose comme :
Les fonctions ne sont pas toutes susceptibles de modifier cette variable. Celle qui peuvent la modifier sont impures. Les fonctions qui ne peuvent pas agir sur la variable sont pures4.
+
Haskell considère l’état du monde comme une variable à passer à main. Mais son type réel est plus proche de celui ci5 :
où a est le type du résultat. Par exemple, une fonction getChar aura le type `World -> (Char, World).
+
Une autre chose à noter est l’astuce pour corriger l’ordre d’évaluation. En Haskell, pour évaluer f a b, vous avez l’embarras du choix :
+
+
évaluer d’abord a puis b puis f a b
+
évaluer d’abord b puis a puis f a b
+
évaluer a et b parallèlement, puis f a b
+
+
Cela vient du fait que nous avons recours à une partie pure du langage.
+
Maintenant, si vous regardez la fonction main, vous voyez tout de suite qu’il faut évaluer la première ligne avant la seconde, car pour évaluer la seconde ligne vous devez utliser un paramètre donné suite à l’évaluation de la première ligne.
+
Cette astuce fonctionne très bien. Le compilateur donnera à chaque étape un pointeur sur l’id du nouveau monde courant. En réalité, print sera évaluée comme suit :
+
+
Écrit quelque chose sur l’écran
+
Modifie l’id du monde
+
renvoyer ((), id du nouveau monde).
+
+
Maintenant, si jetez un oeil au style de la fonction main, vous remarquerez qu’il est clairement peu commode. Essayons de faire la même chose avec la fonction askUser :
Même si pour certaines lignes l’argument x n’est pas nécessaire. La sortie est un couple, (answer, newWorldValue). Chaque fonction f doit avoir un type similaire à :
Avec, bien entendu, actionN w :: (World) -> (a,World).
+
+
IMPORTANT: Il y a seulement 2 schémas importants à considérer :
+
let (x,w1) = action1 w0 in
+let (y,w2) = action2 x w1 in
+
et
+
let (_,w1) = action1 w0 in
+let (y,w2) = action2 w1 in
+
+
+
+
+
Maintenant, préparez-vous pour un petit tour de magie ! Faisons disparaître les variables temporaires de monde courant. Nous allons attacher (NDT: bind en anglais) les deux lignes. Définissons la fonction bind. Son type est assez intimidant au début :
getLine est une action d’E/S qui prend le monde en paramètre et retourne un couple (String, World). Cela peut être résumé par : getLine est de type IO String, que nous pouvons voir comme une action d’E/S qui retournera une chaîne de caractères “dans une E/S”.
+
La fonction print est elle aussi intéressante. Elle prend un argument qui peut être montré avec show. En fait, elle prend deux arguments. Le premier est la valeur et le deuxième est l’état du monde. Elle retourne un couple de type ((), World). Cela signifie qu’elle change l’état du monde, mais ne produit pas d’autre donnée.
+
Ce nouveau type IO a nous aide à simplifier le type de bind :
Comme print est de type Show a => a -> (World -> ((), World)), nous savons que res = () (type unit) Si vous ne voyez pas ce qui est magique ici, essayons avec trois lignes cette fois.
Nous pouvons utiliser une meilleure notation. Utilisons (>>=) au lieu de bind. (>>=) est une fonction infixe, comme (+); pour mémoire : 3 + 4 ⇔ (+) 3 4
D’une manière générale, c’est une façon de mettre des valeurs pures dans le “contexte d’E/S”. Le nom général pour putInIO est return. C’est un plutôt un mauvais nom lorsque vous commencez à programmer en Haskell. return est très différent de ce à quoi vous pourriez être habitué.
Maintenant le secret peut être dévoilé : IO est une monade. Être une monade signifie que vous avez accès à du sucre syntaxique avec la notation do. Mais principalement, vous avez accès à un motif de codage qui tempérera le flux de votre code.
+
+
Remarques importantes :
+
+
Le monades n’ont pas forcément quoi que ce soit à voir avec les effets de bord ! Il y a beaucoup de monades pures.
+
Les monades concernent plus le séquençage.
+
+
+
En Haskell, Monad est une classe de type. Pour être une instance d’une classe de type, vous devez fournir les fonctions (>>=) et return. La fonction (>>) est dérivée de (>>=). Voici commment la classe de type Monad est déclarée (grosso modo) :
le mot-clé class n’est pas votre ami. Une classe en Haskell n’est pas du même genre que celle des langages orientés-objet. Elles ont beaucoup de similarités avec les interfaces de Java. Un meilleur mot aurait été typeClass, ce qui signifierait un ensemble de types. Pour qu’un type appartienne à une classe, toutes les fonctions de cette classe doivent être fournies pour ce type.
+
Dans cet exemple particulier de classe de type, le type m doit être un type qui prend un argument. par exemple IO a, mais aussi Maybe a, [a], etc…
+
Pour être une monade utile, votre fonction doit obéir à quelques règles. Si votre construction n’obéit pas à ces règles, des choses étranges pourraient se produire :
+
+
+
+
return a >>= k == k a
+m >>= return == m
+m >>= (\x -> k x >>= h) == (m >>= k) >>= h
+
+
+Maybe est une monade
+
+
Il y a beaucoup de types différents qui sont des instances de Monad. L’un des plus faciles à décrire est Maybe. Si vous avez une séquence de valeurs Maybe, vous pouvez utiliser les monades pour les manipuler. C’est particulièrement utile pour enlever des constructions if..then..else.. trop nombreuses.
+
Imaginez une opération bancaire complexe. Vous êtes éligible pour gagner 700€ seulement si vous pouvez effectuer une liste d’opérations sans tomber en dessous de zéro.
Nous avons prouvé que les monades sont un bon moyen de rendre notre code plus élégant. Remarquez que cette idée d’organisation de code, en particulier pour Maybe, peut être utilisée dans la plupart des langages impératifs. En fait, c’est le type de construction que nous faisons naturellement.
+
+
Une remarque importante :
+
Le premier élement de la séquence qui sera évalué comme Nothing stoppera l’évaluation. Cela signifie que vous n’exécutez pas toutes les lignes. Cela découle du caractère paresseux de Haskell.
+
+
Vous pourriez aussi revoir ces exemples avec la définition de (>>=) pour Maybe en tête :
La monade Maybe a prouvé par un simple exemple qu’elle est utile. Nous avons vu l’utilité de la monade IO. Mais maintenant, voici un exemple encore plus cool : les listes.
Je ne listerai pas toutes les monades, car il y en a beaucoup. Utiliser les monades simplifie la manipulations de plusieurs notions dans les langages purs. Les monades sont très utiles, en particulier pour :
+
+
L’E/S (IO),
+
les calculs non-déterministes,
+
générer des nombres pseudo-aléatoires,
+
garder un état de configuration,
+
écrire un état,
+
…
+
+
Si vous m’avez suivi jusqu’ici, alors vous avez terminé ! Vous connaissez les monades6 !
Dans la section sur les structures infinies nous avons vu quelques constructions simples. Malheureusement, nous avons enlevé deux propriétés de notre arbre:
+
+
Pas de valeurs identiques
+
Arbre bien ordonné
+
+
Dans cette section nous allons tenter de garder la première propriété. Concernant la seconde, nous ne devons pas nous en préoccuper ici mais nous discuterons de comment la garder le plus possible.
+
+
This code is mostly the same as the one in the tree section.
tournera en boucle pour toujours. Simplement parce que le code essayera d’accéder à première valeur de filter (<1) [2..]. Mais filter n’est pas assez intelligent pour comprendre que le résultat est une liste vide.
+
Toutefois, cela reste un exemple sympa de ce qu’un programme non-stricit a à offrir.
+
Laissé pour exercice au lecteur:
+
+
Prouver l’existence d’un nombre n tel que treeTakeDepth n (treeFromList shuffle) provoquera une boucle infinie.
+
Trouver une borne supérieur n.
+
Prouver qu’il n(y a pas de liste shuffle qui termine le programme pour n’importe quelle profondeur.
Pour résoudre ces problèmes nous allons modifier légèrement nos fonctions treeFromList et shuffle.
+
Un premier problème est le manque de nombres différents dans notre immlémentation de shuffle. Nous avons généré seulement 4331 nombres différents. Pour résoudre cela nous allons faire un meilleure fonction shuffle.
Cette fonction à la propriété de ne pas avoir de bornes supérieure ou inférieure. Mais avoir une meilleure list shuffle n’est pas assez pour entrer dans une boucle infinie.
+
Généralement, nous ne pouvons pas décider que filter (<x) xs est vide. Donc pour résoudre le problème, je vais autoriser quelques erreurs dans la création de notre arbre binaire. Cette nouvelle version du code peut créer des arbres binaires qui n’ont pas à suivre les propriétés suivantes pour quelque uns de leurs noeuds:
+
+
Tous les élements de la branche de gauche doit être strictement inférieur au la valeur racine.
+
+
Remarquez que cela donnera souvent un arbre ordonné. En outre, avec cette construction, chaque noeud est unique dans l’arbre.
+
Voici notre nouvelle version de treeFromList. Nous avons simplement remplacé filter par safefilter.
Cette nouvelle fonction safefilter est presque équivalente à filter mais n’entre pas dans des boucles infinies si le résultat est une liste finie. Si elle ne peut pas trouver un élément pour lequel le test est vrai après 10000 étapes consécutives, alors elle considère que la recherche est finie.
Vous devriez réaliser que le temps nécessaire pour afficher chaque valeur est différent. C’est parce que Haskell calcule chaque valeur lorsqu’il en a besoin. Et dans ce cas, il est demandé de l’afficher à l’écran.
+
Vous pouvez même essayer de remplacer la profondeur de 8 par 100. Cela marchera sans tuer votre RAM! La gestion de la mémoire est faite naturellement par Haskell.
+
Laissé comme exercices au lecteur:
+
+
Même avec une grande valeur constante pour deep et nbTry, cela semble marcher correctement. Mais dans le pire des cas, cela peut devenir exponentiel. Créez la pire liste à donner comme paramètre à treeFromList. indice: pensez à ([0,-1,-1,....,-1,1,-1,...,-1,1,...]).
+
J’ai commencé à implémenter safefilter comme ceci:
+
+safefilter' f l = if filter f (take 10000 l) == []
+ then []
+ else filter f l
+
+Expliquer pourquoi cela ne fonctionne pas et peut entrer dans une boucle infinie.
+
Supposez que shuffle est une liste de nombre réellement aléatoires avec de plus en plus de bornes. Si vous étudiez un peu cette structure, vous découvrirez qu’elle a toutes les chances d’être finie. En utilisant le code suivant (supposez que nous pouvons utliser safefilter' directement comme si cela n’était pas dans le where de safefilter. trouvez une définition de f telle que, avec une probabilité de 1, treeFromList' shuffle est infinie?. Et prouvez-le. Avertissement, ce n’est qu’une conjecture.
Particulièrement, je voudrais remercier mille fois Emm pour le temps qu’il a consacré à corriger mon anglais. Merci beaucoup.
+
+
+
+
Même si tous les langages récents essayent de les cacher, ils restent présents.↩
+
Je sais que je triche. Mais je parlerais de la non-rigueur plus tard. ↩
+
Qui est elle-même très similaire à la fonction eval de javascript, appliquée sur une chaîne contenant du code au format JSON.↩
+
Il y a quelques exceptions peu sûres à cette règle. Mais vous ne devriez pas en voir en application réelle, sauf pour le debugging.↩
+
Pour les curieux, le vrai type est data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}. Tous les # ont rapport avec l’optimisation et j’ai échangé quelques champs dans mon exemple. Mais c’est l’idée de base.↩
+
Vous aurez quand même besoin de pratiquer un peu pour vous habituer à elles et pour comprendre quand les utiliser et créer les vôtres. Mais vous avez déjà fait un grand pas dans cette direction.↩
Few days ago there were about 20 job offer for Haskell. In only one day! How is that possible? As a real haskeller, I find this situation unbearable!
+
After all, we must avoid success at all cost. And I’ll help SPJ achieve this honorable goal.
+
+
Prevent Interest from beginner
+
Imagine a situation were you see people demonstrating some interest in learning Haskell.
+
Quick! Prevent them from going further.
+
If they come from the dynamic (uni-typed) languages like Python, Javascript…:
+
+
Haskell? A statically typed language??? Hmm… You mean like C and Java?
+
+
Such a remark should immediately shut down any interest in Haskell.
+
If they want to produce application with them:
+
+
Haskell? Isn’t it only a language for student! I don’t think it is useful for REAL WORLD applications!
+
+
If they just want to learn something new:
+
+
Haskell? Ah yes, I remember, mostly they only have the equivalent of Java interfaces and they stopped there. They don’t even have classes!!!! Can you imagine? I don’t even speak about class inheritance.
+
We’re in 2016! And they don’t even support basic Object Oriented Programming. What a joke!
+
+
If they love low level programming:
+
+
Haskell? Ah yes, I heard that lazyness make it impossible to think about code complexity and generally cause lot of space leaks.
+
+
And if it is not enough:
+
+
Haskell? Ah yes. I’m not fan of their Stop the World GC.
+
+
If they come from LISP and the statically typed language remark wasn’t enough. Try to mention the lack of macros in Haskell. Don’t mention template Haskell or even less Generics and all recent progress in GHC.
+
Make it difficult to install
+
Many hints there:
+
+
Send them on another compiler than GHC
+
Explain that they should never use a binary distribution of GHC! And they must compile it manually! It might not stop them but it will make the installation process much more tedious.
+
Lie! Explain there is a severe security issue with latest tools. Explain they must use cabal-install 1.18 or older.
+
Also explain them that in order to be able to handle lib dependencies correctly they MUST first learn Nix! Never talk about stack, cabal freeze, … While Nix is great, forcing new user completely alien to all these concepts to first learn it before starting to write their first line of code can greatly reduce their enthusiasm. Bonus point if you make them believe you can only program in Haskell on NixOS.
+
+
Make it difficult to learn
+
Make new comers feel dumb
+
The very first thing to do is to explain how Haskell is so easy to learn. How natural it is for everybody you know. And except someone you always considered very dumb, everybody was very productive in Haskell in few hours.
+
Use vocabulary alien to them as much as possible. Here is a list of terms you should use in the very first minutes of your description of Haskell:
+
+
catamorphism (bonus if you mention that the word come from the Greek κατα for catastrophe, that way you’ll look like a snob and you also use the word catastrophe in a Haskell context).
+
Monad! Of course you should use it ASAP! And explain they are exactly like potatoes or bananas shaped chairs. Double bonus if you explain that monad are really simple as they are just a monoid in the category of endofunctors.
+
GADTs
+
Yoneda Lemma
+
Homotopy Type Theory
+
…
+
+
Each of this term will hopefully be intimidating.
+
Tutorial authors
+
Please don’t provide an obvious first example like:
This nice example should overflow the number of new concepts a Haskell newcomer should deal with:
+
+
Language extensions. Each extension can take a lot of time to be explained.
+
Strange notations:
+
+
:<|>
+
'[] instead of []
+
+
Proxy
+
Immediate usage of $
+
deriving ha ha! You’ll need to explain typeclasses first!
+
the definition for getItemById
+
+
Of course use the most of your energy explaining the language extensions first. Use a great deal of details and if possible use as much as possible references to Category Theory. You’ll get bonus points if you mention HoTT! Double bonus points if you explain that understanding all details in HoTT is essential to use Haskell on a daily basis.
+
Explain that what this does is incredible but for the wrong reasons. For example don’t mention why instance ToJSON Item is great. But insist that we achieved to serve a JSON with extreme elegance and simplicity. Keep insisting on the simplicity and forgot to mention type safety which is one of the main benefit of Servant.
+
If you’re afraid that this example might be too close to a real world product, you can simply use some advanced lenses examples:
Certainly a great example to start a new language with.
+
Library authors
+
+
Do your best not to respect versioning number policy to maximize the probability to break things.
+
Don’t write any documentation, type are enough!
+
Even better, add mistakes to your documentation
+
Each time you can use a meaningful notation, make it wrong. For example, if you have a symmetric relation use an asymmetric symbol to represent it.
+
If possible remove all function names and only use symbols of at least 5 letters: For example you can replace your function log :: Level -> String -> IO () by (<=.=$$.).
+
+
If the the trends continue toward growth, then we might need to go further at the risk of breaking our own ecosystem:
+
+
Split your libs as much as possible. The best would be to use one lib by symbol
+
Use unsafePerformIO as much as possible
+
Push to Hackage a version not accessible on your public repository
+
modify the package on Hackage using the same version but with incompatible API
+
Add memory leaks
+
Add bugs
+
Add back doors and publish how to use them
+
+
Yes we said, at all cost!
+
Conclusion & Mistake
+
So with all of this I believe we should be on the right track to avoid success at all cost!
+
Sorry? What?
+
Oh… Apparently I made a precedence mistake!
+
SPJ didn’t asked to avoid success $ at all cost but to avoid $ success at all cost1.
+
Sorry! My bad! Forget about all of this. Keep the good work everybody! Haskell is certainly one of the most awesome language in the world! Its community is also just great.
+
I’m really happy to see it growth every year. Thanks to all contributors making it possible to still have a lot of fun after many years using Haskell!
+
And the fact that in Haskell the right choice is preferred to the easiest choice, certainly helped.
Avec cette dernière version vous pouvez utiliser map si vous utilisez des fonctions déclarées. mapl pour les fonctions anonymes et mapa pour les fonctions arithmétiques.
Tout d’abord, pourquoi c’est important d’avoir ces fonctions. Plus je programmais avec zsh plus j’essayais d’avoir un style fonctionnel.
+
Le minimum pour pouvoir avoir du code plus lisible c’est de posséder les fonctions map, filter et fold.
+
Voici pourquoi avec une comparaison. Commençons par un programme qui converti tous les gif en png dans plusieurs répertoires projets contenant tous des répertoires resources. Avant :
La première version peu paraître plus facile à lire. Mais la seconde est plus bien supérieure en terme d’architecture. Je ne veux pas discuster ici pourquoi c’est mieux. Je vous demande simplement de me croire quand je dis que l’approche fonctionnelle est supérieure.
Un exemple de programmation en Haskell de bout en bout
+
+
+
+
+
+
+
+
+
+
tlpl: Apprenez comment commencer un nouveau projet Haskell. Avec en exemple le créateur de projet Haskell lui-même.
+
+
“Good Sir Knight, will you come with me to Camelot, and join us at the Round Table?”
+
+
In order to work properly with Haskell you need to initialize your environment. Typically, you need to use a cabal file, create some test for your code. Both, unit test and propositional testing (random and exhaustive up to a certain depth). You need to use git and generally hosting it on github. Also, it is recommended to use cabal sandboxes. And as bonus, an auto-update tool that recompile and retest on each file save.
+
In this article, we will create such an environment using a zsh script. Then we will write a Haskell project which does the same work as the zsh script. You will then see how to work in such an environment.
+
If you are starting to understand Haskell but consider yourself a beginner, this tutorial will show you how to make a real application using quite surprisingly a lot of features:
+
+
use colorized output
+
interact with a user in command line
+
read/write files
+
kind of parse a file (in fact, simply split it)
+
use a templating system (mustache: fill a data structure, write files)
+
make a HTTP GET request then parse the JSON answer and use it
+
use random
+
create a cabal package
+
add and use non source files to a cabal package
+
Test your code (both unit testing and property testing)
+
+
☞ zsh is by its nature more suitable to file manipulation. But the Haskell code is clearly more organized while quite terse for a multi-purpose language.
+
☞ holy-project is on hackage. It can be installed with cabal update && cabal install holy-project.
While the article is very good, I lacked some minor informations1. Inspired by it, I created a simple script to initialize a new Haskell project. During the process I improved some things a bit.
What does this script do that cabal init doesn’t do?
+
+
Use cabal sandbox
+
It initialize git with the right .gitignore file.
+
Use tasty to organize your tests (HUnit, QuickCheck and SmallCheck).
+
Use -Wall for ghc compilation.
+
Will make references to Holy Grail
+
Search your default github username via github api.
+
+
zsh really?
+
+
+
+
Developing the script in zsh was easy. But considering its size, it is worth to rewrite it in Haskell. Furthermore, it will be a good exercise.
+
Patricide
+
In a first time, we initialize a new Haskell project with holy-haskell.sh:
+
+> ./holy-haskell.sh
+Bridgekeeper: Stop!
+Bridgekeeper: Who would cross the Bridge of Death
+Bridgekeeper: must answer me these questions three,
+Bridgekeeper: ere the other side he see.
+You: Ask me the questions, bridgekeeper, I am not afraid.
+
+Bridgekeeper: What is the name of your project?
+> Holy project
+Bridgekeeper: What is your name? (Yann Esposito (Yogsototh))
+>
+Bridgekeeper: What is your email? (Yann.Esposito@gmail.com)
+>
+Bridgekeeper: What is your github user name? (yogsototh)
+>
+Bridgekeeper: What is your project in less than ten words?
+> Start your Haskell project with cabal, git and tests.
+Initialize git
+Initialized empty Git repository in .../holy-project/.git/
+Create files
+ .gitignore
+ holy-project.cabal
+ Setup.hs
+ LICENSE (MIT)
+ test/Test.hs
+ test/HolyProject/Swallow/Test.hs
+ src/HolyProject/Swallow.hs
+ test/HolyProject/Coconut/Test.hs
+ src/HolyProject/Coconut.hs
+ src/HolyProject.hs
+ src/Main.hs
+Cabal sandboxing, install and test
+...
+ many compilations lines
+...
+Running 1 test suites...
+Test suite Tests: RUNNING...
+Test suite Tests: PASS
+Test suite logged to: dist/test/holy-project-0.1.0.0-Tests.log
+1 of 1 test suites (1 of 1 test cases) passed.
+All Tests
+ Swallow
+ swallow test: OK
+ coconut
+ coconut: OK
+ coconut property: OK
+ 148 tests completed
+
+All 3 tests passed
+
+
+
+Bridgekeeper: What... is the air-speed velocity of an unladen swallow?
+You: What do you mean? An African or European swallow?
+Bridgekeeper: Huh? I... I don't know that.
+[the bridgekeeper is thrown over]
+Bridgekeeper: Auuuuuuuuuuuugh
+Sir Bedevere: How do you know so much about swallows?
+You: Well, you have to know these things when you're a king, you know.
+
+
The different steps are:
+
+
small introduction quotes
+
ask five questions – three question sir…
+
create the directory for the project
+
init git
+
create files
+
sandbox cabal
+
cabal install and test
+
run the test directly in the terminal
+
small goodbye quotes
+
+
Features to note:
+
+
color in the terminal
+
check some rules on the project name
+
random message if error
+
use ~/.gitconfig file in order to provide a default name and email.
+
use the github API which returns JSON to get the default github user name.
+
+
So, apparently nothing too difficult to achieve.
+
We should now have an initialized Haskell environment for us to work. The first thing you should do, is to go into this new directory and launch ‘./auto-update’ in some terminal. I personally use tmux on Linux or the splits in iTerm 2 on Mac OS X. Now, any modification of a source file will relaunch a compilation and a test.
The haskell version is made by hand where zsh already had a capitalize operation on string with many words. Here is the difference between the shell and haskell way (note I splitted the effect of concatMap as map and concat):
In Haskell, while possible, we shouldn’t put the file content in the source code. We have a relatively easy way to include external file in a cabal package. This is what we will be using.
+
Furthermore, we need a templating system to replace small part of the static file by computed values. For this task, I choose to use hastache, a Haskell implementation of Mustache templates3.
+
Add external files in a cabal project
+
Cabal provides a way to add files which are not source files to a package. You simply have to add a Data-Files: entry in the header of the cabal file:
Now we simply have to create our files at the specified path. Here is for example the first lines of the LICENSE file.
+
The MIT License (MIT)
+
+Copyright (c) {{year}}{{author}}
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+...
+
It will be up to our program to replace the {{year}} and {{author}} at runtime. We have to find the files. Cabal will create a module named Paths_holy_project. If we import this module we have the function genDataFileName at our disposal. Now we can read the files at runtime like this:
A first remark is for portability purpose we shouldn’t use String for file path. For example on Windows / isn’t considered as a subdirectory character. To resolve this problem we will use FilePath:
In order to use hastache we can either create a context manually or use generics to create a context from a record. This is the last option we will show here. So in a first time, we need to import some modules and declare a record containing all necessary informations to create our project.
We use external files in mustache format. We ask question to our user to fill a data structure. We use this data structure to create a context. Hastache use this context with the external files to create the project files.
+
Git and Cabal
+
+
+
+
We need to initialize git and cabal. For this we simply call external command with the system function.
We want to retrieve the ~/.gitconfig file content and see if it contains a name and email information. We will need to access to the HOME environment variable. Also, as we use bytestring package for hastache, let’s take advantage of this library.
We could notice, getNameAndMail doesn’t read the full file and stop at the first occurrence of name and mail.
+
Use the github API
+
+
+
+
The task seems relatively easy, but we’ll see there will be some complexity hidden. Make a request on https://api.github.com/search/users?q=<email>. Parse the JSON and get the login field of the first item.
+
So the first problem to handle is to connect an URL. For this we will use the http-conduit package.
But, after some research, I discovered we must declare an User-Agent in the HTTP header to be accepted by the github API. So we have to change the HTTP Header, and our code became slightly more complex:
So now, we have a String containing a JSON representation. In javascript we would have used login=JSON.parse(body).items[0].login. How does Haskell will handle it (knowing the J in JSON is for Javascript)?
+
First we will need to add the lens-aeson package and use it that way:
It looks ugly, but it’s terse. In fact each function (^?), key and nth has some great mathematical properties and everything is type safe. Unfortunately I had to make my own jsonValueToString. I hope I simply missed a simpler existing function.
The HolyProject.hs file contains mostly the code that ask questions, show errors and copy files using hastache.
+
One of the benefits in modularizing the code is that our main code is clearer. Some functions are declared only in a module and are not exported. This help us hide technical details. For example, the modification of the HTTP header to use the github API.
+
Documenting
+
+
+
+
We didn’t take much advantage of the project structure yet. A first thing is to generate some documentation. Before most function I added comment starting with -- |. These comment will be used by haddock to create a documentation. First, you need to install haddock manually.
And magically, you’ll have a documentation in dist/doc/html/holy-project/index.html.
+
Tests
+
While the Haskell static typing is quite efficient to prevent entire classes of bugs, Haskell doesn’t discard the need to test to minimize the number of bugs.
+
Unit Testing with HUnit
+
+
+
+
It is generally said to test we should use unit testing for code in IO and QuickCheck or SmallCheck for pure code.
+
A unit test example on pure code is in the file test/HolyProject/Swallow/Test.hs:
Note swallow is (++). We group tests by group. Each group can contain some test suite. Here we have a test suite with only one test. The (@=?) verify the equality between its two parameters.
+
So now, we could safely delete the directory test/HolyProject/Swallow and the file src/HolyProject/Swallow.hs. And we are ready to make our own real world unit test. We will first test the module HolyProject.GithubAPI. Let’s create a file test/HolyProject/GithubAPI/Test.hs with the following content:
You have to modify your cabal file. More precisely, you have to add HolyProject.GithubAPI in the exposed modules of the library secion). You also have to update the test/Test.hs file to use GithubAPI instead of Swallow.
+
So we have our example of unit testing using IO. We search the github nickname for some people I know and we verify github continue to give the same answer as expected.
+
Property Testing with SmallCheck and QuickCheck
+
+
+
+
When it comes to pure code, a very good method is to use QuickCheck and SmallCheck. SmallCheck will verify all cases up to some depth about some property. While QuickCheck will verify some random cases.
+
As this kind of verification of property is mostly doable on pure code, we will test the StringUtils module.
+
So don’t forget to declare HolyProject.StringUtils in the exposed modules in the library section of your cabal file. Remove all references to the Coconut module.
+
Modify the test/Test.hs to remove all references about Coconut. Create a test/HolyProject/StringUtils/Test.hs file containing:
+All Tests
+ StringUtils
+ SC projectNameFromString idempotent: OK
+ 206 tests completed
+ SC capitalize idempotent: OK
+ 1237 tests completed
+ QC projectNameFromString idempotent: FAIL
+ *** Failed! Falsifiable (after 19 tests and 5 shrinks):
+ "a a"
+ Use --quickcheck-replay '18 913813783 2147483380' to reproduce.
+ GithubAPI
+ Yann: OK
+ Jasper: OK
+
+1 out of 5 tests failed
+
+
The test fail, but this is not an error. Our capitalize function shouldn’t be idempotent. I simply added this test to show what occurs when a test fail. If you want to look more closely to the error you could do this:
It is important to use ./interact instead of ghci. Because we need to tell ghci how to found the package installed.
+
Apparently, SmallCheck didn’t found any counter example. I don’t know how it generates Strings and using deeper search is really long.
+
Conclusion
+
+
+
+
Congratulation!
+
Now you could start programming in Haskell and publish your own cabal package.
+
+
+
+
For example, you have to install the test libraries manually to use cabal test.↩
+
There is no easy way to do something like name=$(ask name). Simply because $(ask name) run in another process which doesn’t get access to the standard input↩
+
Having a good level of power in templates is very difficult. imho Mustache has made the best compromise.↩
tlpl: Vous désirez apprendre vim (le meilleur editeur de texte connu à ce jour) le plus rapidement possible. Voici mes conseils pour vous aider. Commencez à apprendre le minimum vital, puis apprenez doucement de nouvelles commandes.
Apprenez vim et ce sera votre dernier éditeur. Aucun éditeur que je connaisse ne le surpasse. Sa prise en mais est difficile, mais payante.
+
Je vous conseille de l’apprendre en 4 étapes :
+
+
La survie
+
Se sentir à son aise
+
Se sentir meilleur, plus fort et plus rapide
+
Tirer parti des super-pouvoirs de vim
+
+
À la fin de ces leçons vous serez transformé.
+
Avant de commencer, un message pour vous prévenir. Apprendre vim sera difficile au début. Ça prendra du temps. Vous devrez vous entraîner. Apprendre vim ressemble beaucoup à apprendre un instrument de musique. N’espérez pas être plus efficace avec vim qu’avec un autre éditeur avant au moins trois jours. En fait ça sera certainement plus 2 semaines que 3 jours.
Dans un éditeur normal, il suffit de taper sur une touche du clavier et la lettre s’affiche à l’écran. Pas ici. Vim est en mode Normal. Commençons par placer vim en mode Insert. Tapez sur la touche i.
+
Voilà, c’est magique. Vous pouvez tapez comme dans un éditeur standard. Pour repasser en mode Normal tapez sur la touche Echap.
+
Maintenant que vous savez passer du mode Normal au mode Insert. Voici les commandes de survie (toutes en mode Normal) :
+
+
+
i → Passer en mode insértion. Taper Echap pour repasser en mode Normal.
+
x → Supprimer le caractère sous le curseur
+
:wq → Sauvegarder et quitter (:w sauvegarde, :q<enter> quitter)
+
dd → Supprimer (et copier) la ligne courante
+
p → Coller
+
+
Récommandées :
+
+
hjkl (optionnel) → se déplacer (<-↓↑→). Souvenez vous j ressemble à une flèche vers le bas.
+
:help <commande> → Affiche l’aide pour <commande>. Vous pouvez aussi écrire :help pour atterir sur l’aide générale.
+
+
+
Seulement 5 commandes. Voilà, c’est tout pour un début. Essayez d’éditer vos fichiers comme ça pendant une petite journée. Lorsque ces commandes vous sembleront naturelles, vous pourrez passer à l’étape d’après.
+
Mais avant un petit mot sur le mode Normal. Dans un éditeur normal pour copier il faut utiliser une combinaison de touches (Ctrl-c). En fait, lorsque vous appuyez sur la touche Ctrl, c’est un peu comme si toutes les touches du clavier avaient un autre usage. Dans vim, lorsque vous êtes en mode Normal, c’est comme si vous mainteniez Ctrl enfoncé.
+
Quelques mots concernant les notations :
+
+
Au lieu d’écrire Ctrl-λ, j’écrirai <C-λ>.
+
Les commandes qui commencent par : ont un retour à la ligne implicite à la fin. Par exemple lorsque que j’écris, :q celà signifi qu’il faut taper :, suivi de q, suivi de <Return>.
+
+
2ème Niveau – Se sentir à son aise
+
Vous connaissez les commandes de survie. Passons à des commandes pour être un peu plus à notre aise. Je vous suggère :
+
+
Les variantes de l’insertion
+
+
+
a → Comme i, mais après la position du curseur.
+
o → Comme i, mais à la ligne suivante.
+
O → Comme o mais ajoute la ligne avant.
+
cw → Remplacer la fin du mot.
+
+
+
Déplacements basiques
+
+
+
0 → Aller à la première colonne.
+
^ → Aller au premier caractère de la ligne.
+
$ → Aller à la fin de la ligne.
+
g_ → Aller au dernier caractère de la ligne.
+
/pattern → Rechercher pattern dans le fichier.
+
+
+
Copier/Coller
+
+
+
P → Coller avant. Souvenez vous, p colle après la position du curseur.
+
yy → Copier la ligne courante. C’est plus simple et équivalent à ddP
+
+
+
Annuler/Refaire
+
+
+
u → Annuler (undo)
+
<C-r> → Refaire
+
+
+
Ouvrir/Sauvegarder/Quitter/Changer de fichier (buffer)
+
+
+
:e <path/to/file> → Ouvrir.
+
:w → Sauvegarder.
+
:saveas <path/to/file> → Sauvegarder sous …
+
:x, ZZ ou :wq → Sauvegarder et quitter (:x sauvegarde seulement si nécessaire).
+
:q! → Quitter sans sauvegarder. De même :qa! quitte même si d’autres fichiers (buffers) ont des modifications non sauvegardées.
+
:bn (resp. :bp) → Affiche le fichier suivant (resp. précédent).
+
+
+
+
Prenez le temps de bien intégrer ces commandes. Une fois fait, vous devriez être capable de faire tout ce qu’on peut attendre d’un éditeur de texte classique.
+
3ième Niveau – Meilleur. Plus fort. Plus rapide.
+
Bravo ! Si vous êtes arrivé jusqu’ici nous allons pouvoir commencer à apprendre les choses vraiment intéressantes. Pour cette section, je vais seulement parler de commandes disponible dans vi et vim. Vim est la contraction de “vi improved”, ou en Français, “vi amélioré”.
+
Meilleur
+
Voyons comment nous pouvons éviter les répétitions avec vi :
+
+
. → Le caractère point répètera la dernière commande. Très utile.
+
N<commande> → répètera la commande N fois.
+
+
Quelques exemples, ouvrez un fichier (non vide) avec vim et tapez :
. → Juste après la dernière commande réécrira les 100 “desu”.
+
3. → Écrira 3 “desu” et non pas 300. Bien vu n’est-ce pas ?
+
+
+
Plus fort
+
Savoir se déplacer efficacement avec vim est très important. Ne sautez pas cette section.
+
+
NG → Aller à la ligne N
+
gg → raccourci pour 1G, retourner au début du fichier
+
G → Aller à la dernière ligne.
+
Déplacement autour des mots:
+
+
+
w → aller au début du mot suivant
+
e → aller à la fin du mot courant
+
+
Par défaut les mots sont seulement composés de lettres (et du caractère souligné _). Appelons un MOT un ensemble de lettre séparé par des caractères blancs (espaces, tabulation). Si vous voulez considérer des MOTS alors il suffit d’utiliser les majuscules.
+
+
W → aller au début du MOT suivant
+
E → aller à la fin du MOT courant
+
+
+
+
+
+
+
Passons aux commandes de déplacement les plus efficaces :
+
+
+
% : Aller à la parenthèse, accolade, crochet correspondante.
+
* (resp. #) : Aller à la prochaine (resp. précédente) occurrence du mot sous le curseur
+
+
+
Croyez moi, ces trois dernières commandes valent de l’or. Retenez les et vous gagnerez beaucoup de temps.
+
Plus rapide
+
Vous vous souvenez que j’ai dit que les déplacements étaient très importants en vi. Voilà pourquoi. Une façon de travailler avec vim est de se dire que l’on fait des “phrases”. Le verbe étant la commande et les compléments définissent la zone d’action. De façon générale :
+
<position de depart><commande><position d'arrivee>
+
Par exemple : 0y$ signifie :
+
+
0 → Aller au début de la ligne,
+
y → copie à partir d’ici,
+
$ → jusqu’à la fin de cette ligne.
+
+
On peut donc faire des choses comme ye, copie à partir de la position courante du curseur jusqu’à là fin du mot. Mais aussi: y2/toto copie jusqu’à la seconde prochaine occurrence de “toto”.
+
Ce qui est vrai pour y (yank → copier), est aussi vrai pour d (delete → supprimer), v (sélection visuelle), gU (uppercase → majuscule),gu (lowercase → minuscule), etc…
+
4ième Niveau – Les super pouvoirs de Vim
+
Jusqu’ici vous avez appris les commandes les plus courantes. Mais voici les killer features de vim. Celles que je n’ai retrouvé que dans vim (ou presque).
+
Déplacement sur la ligne : 0^$g_fFtT,;
+
+
+
0 → aller à la colonne 0,
+
^ → aller au premier caractère de la ligne
+
$ → aller à la dernière colonne de la ligne
+
g_ → aller au dernier caractère de la ligne
+
fa → vous amène à la prochaine occurrence de a sur la ligne courante. , (resp. ;) recherche l’occurrence suivante (resp. précédente).
+
t, → vous amène juste avant le ,.
+
3fa → recherche la 3ième occurrence de a.
+
F et T → comme f et t mais en arrière.
+
+
+
+
+
+
Un truc pratique : dt" → supprime tout jusqu’au prochain ".
+
Selection de zone <action>a<object> ou <action>i<object>
+
Ces commandes sont utilisable seulement en mode visuel ou après un “opérateur”. Mais elles sont très puissantes. Leur forme générale est:
+
<action>a<objet> et <action>i<objet>
+
Où action peut être par exemple d (delete), y (yank), v (select in visual mode), etc… Un objet peut être: w un mot, W un MOT (mot étendu), s une phrase, p un paragraphe. Mais aussi des caractère plus naturels comme ", ', ), }, ].
+
Supposons que le curseur soit positionné sur le premier o dans (map (+) ("foo")).
+
+
+
vi" → sélectionnera foo.
+
va" → sélectionnera "foo".
+
vi) → sélectionnera "foo".
+
va) → sélectionnera ("foo").
+
v2i) → sélectionnera map (+) ("foo")
+
v2a) → sélectionnera (map (+) ("foo"))
+
+
+
+
+
+
Sélection de blocs rectangulaires : <C-V>.
+
Les blocs rectangulaires sont très commodes pour commenter plusieurs lignes de codes. Typiquement: ^<C-V><C-d>I-- [ESC]
+
+
^ → aller au premier caractère de la ligne
+
<C-V> → Commencer la sélection du bloc
+
<C-d> → se déplacer vers le bas (pourrait être jjj ou % etc…)
+
I-- [ESC] → écrit -- pour commenter le reste de la ligne.
+
+
+
+
+
Remarquez que sous windows, vous devez utiliser <C-q> plutôt que <C-v> si votre “presse papier” n’est pas vide.
+
Complétion : <C-n> et <C-p>.
+
En mode Insert, commencez à écrire le début d’un mot déjà présent dans l’un des buffers (fichers) ouvert et tapes <C-p>. Magique.
+
+
+
+
Macros : qa faire quelque chose q, @a, @@
+
qa enregistre tout ce que vous faite et enregistre le tout dans le registrea. Ensuite @a va rejouer la macro enregistrée dans le registre a comme si c’est vous qui tapiez au clavier. @@ est un raccourci pour rejouer la dernière macro exécutée.
+
+
Exemple : Sur une ligne contenant seulement un 1 tapez :
+
+
qaYp<C-a>q →
+
+
qa → début de l’enregistrement.
+
Yp → copier cette ligne.
+
<C-a> → incrémente le nombre.
+
q → arrête d’enregistrer.
+
+
@a → écrit un 2 sous le 1.
+
Écrivez 100@@. Cela va créer une liste de nombre croissants jusqu’à 103.
+
+
+
+
+
+
Sélection visuelle : v,V,<C-v>
+
On a déjà vu un exemple avec <C-V>. Mais il y a aussi, v et V. Et une fois la sélection visuelle faite vous pouvez par exemple:
+
+
J → joindre toutes les lignes pour en faire une seule
+
< (resp. >) → indenter à gauche (resp. à droite).
+
= → auto indenter
+
+
+
+
+
Ajouter quelque chose à la fin de toutes les lignes sélectionnées visuellement :
+
+
<C-v>
+
aller jusqu’à la ligne désirée (jjj ou <C-d> ou /pattern ou % etc…)
+
$ aller à la fin
+
A, écrire le texte, Echap.
+
+
+
+
+
Splits : :split et vsplit.
+
Je vous conseille de faire un :help split. Celà permet de manipuler plusieurs buffer sur la même fenêtre. Voici les commandes principales :
+
+
+
:split → crée un split (:vsplit crée un split vertical)
+
<C-w><dir> → où dir est l’un de hjkl ou ←↓↑→ permet de changer de split.
+
<C-w>_ (resp. <C-w>|) → Maximise la taille du split (resp. split vertical)
+
<C-w>+ (resp. <C-w>-) → Agrandi (resp. diminue) le split
+
+
+
+
+
+
Conclusion
+
Voilà, je vous ai donné 90% des commandes que j’utilise tous les jours. N’essayez pas de toutes les apprendre en une journée. Il faut le temps de s’habituer à chaque nouvelle commande. Je vous conseille de ne pas apprendre plus d’une ou deux commandes par jour.
+
Apprendre Vim est plus une question d’entraînement que de mémorisation. Heureusement vim est founi avec un très bon tutoriel et une excellente documentation. Lancez vimtutor jusqu’à ce que vous vous sentiez à l’aise avec les commandes basiques. De plus, vous devriez aussi lire en détail la page suivate : :help usr_02.txt.
+
Ensuite vous découvrirez !, les folds, les registres, les plugins et tout un tas d’autres choses. Apprenez vim comme vous apprendriez le piano et vous devriez très bien vous en sortir.
tlpl: Une méthode de gestion des mots de passes que j’utilise avec succès depuis quelques années.
+sha1( mot_de_passe + nom_de_domaine )
+Je ne mémorise qu’un seul mot de passe de très bonne qualité. J’utilise des mots de passe différents sur tous les sites.
+
+
Avant de commencer, je tiens à préciser qu’il s’agit d’une tentative de vous vendre mon appli iPhone ;-).
+
Vous êtes toujours là ? Bon, d’accord, même si vous ne téléchargez pas mon application vous pouvez quand même utiliser ma méthode. Elle est à la fois très sûre et simple à utiliser.
Pourquoi devriez-vous utiliser un gestionnaire de mot de passe ?
+
+
Même les paranoïaques peuvent avoir des ennemis.
+
+
Imaginez que vous trouviez un très bon mot de passe. Vous l’utilisez sur gmail, amazon, PayPal, twitter, facebook… Plus tard, vous découvrez un super petit jeu en ligne très sympa. Vous devez vous enregistrer pour y jouer. Le site vous demande votre email et un mot de passe. Quelques semaines/mois se passent. La machine qui héberge le jeu en ligne se fait attaquer. Maintenant, l’attaquant du site web possède votre email avec ce mot de passe. Il peut donc essayer votre mot de passe un peu partout. Sur PayPal par exemple.
+
Bien, maintenant comment pouvons nous régler ce problèmes ?
+
Quelle méthodologie ?
+
+
Le bon, la brute et le truand
+
+
La méthode la plus courante est de se souvenir de plusieurs mot de passes différents. En général, si vous avez bonne mémoire vous pouvez mémoriser jusqu’à 13 mots de passes. Certain de bonne qualité, d’autre moins.
+
Que faire si vous utilisez plus de services que vous pouvez mémoriser de mots de passe ?
+
Un mauvaise solution peut être de choisir ses mots de passes de la façon suivante :
+
+
twitter: P45sW0r|)Twitter
+
gmail: P45sW0r|)gmail
+
badonlinegame: P45sW0r|)badonlinegame
+
+
Malheureusement, si quelqu’un récupère votre mot de passe sur badonlinegame, il peut facilement retrouvez vos autres mots de passe. Bien sûr, on peut imaginer des transformation de mots de passe de meilleure qualité. Mais il est très difficile d’en trouver une suffisamment bonne.
+
Fort heureusement, il existe une des fonctions bien connues dans le milieu de la sécurité informatique et qui résolvent précisément ce problème. Il s’agit des fontions de hachages. Il est difficile de retrouver le paramètre d’entrée d’une fonction de hachage à partir de son résultat. Prenons un exemple :
+
Si quelqu’un possède 9f00fd5dbba232b7c03afd2b62b5fce5cdc7df63, il va avoir de grande difficulté pour retrouver P45sW0r|).
+
Choisisson la fonction de hashage sha1. Connaissant celà, le mot de passe d’un site donné doit avoir la forme :
+
Où :
+
+
master_password est votre unique mot de passe maître ;
+
domain_name est le nom de domaine du site pour lequel vous voulez le mot de passe.
+
+
+
Il faut aussi penser à certaines contraintes. Certains site web veulent des mots de passe d’une certaine longueur, ni trop longs ni trop courts. Que faire si vous voulez changez votre mot de passe ? Soit parce qu’il est compromis ou simplement parce qu’on vous impose de le changer. C’est pouquoi pour chaque site on a besoin de quelques paramètres supplémentaires.
+
+
le nom de login ;
+
la longueur du mot de passe ;
+
le numéro du mot de passe (pour le changer au cas où) ;
+
le format du mot de passe : hexadécimal ou base64.
+
+
En pratique ?
+
Selon ma situation, voici les outils que j’ai fait et que j’utilise :
Quelquesoit mon environnement de travail, tous mes mots de passes sont à un copier/coller. Pour certain services, j’utilise des mots de passe de 40 caractères. Actuellement j’utilise plutôt des mots de passes de 10 caractères. Avec des mots de passes plus petit, il est encore plus difficile pour un attaquant de retrouver mon mot de passe principal.
+
Je serai heureux de savoir ce que vous pensez de cette méthode. Alors n’hésitez pas à laisser un commentaire ou à m’envoyer un mail.
tlpl: Comment déterminer de la façon la plus rationnelle possible le meilleur framework work relativement à vos besoins. Cliquez ici pour aller au résultats. Cet article n’est disponible qu’en anglais.
+
+
This is it.
+You’ve got the next big idea.
+You just need to make a very simple web application.
+
It sounds easy! You just need to choose a good modern web framework, when suddenly:
+
+
After your brain stack overflowed, you decide to use a very simple methodology. Answer two questions:
+
Which language am I familiar with?
+What is the most popular web framework for this language?
+
Great! This is it.
+
But, you continually hear this little voice.
+
+
“You didn’t made a bad choice, yes. But …
+you hadn’t made the best either.”
+
+
This article try to determine in the most objective and rational way the best(s) web framework(s) depending on your needs. To reach this goal, I will provide a decision tool in the result section.
+
I will use the following methodology:
+
Methodology
+
+
Model how to make choice
+
+
choose important parameters
+
organize (hierarchize) them
+
write down an objective chooser
+
+
Grab objective quantified informations about web frameworks relatively to choosen parameters
+
Sanitize your data in order to handle imprecisions, lack of informations…
+
Apply the model of choice to your informations
+
+
+
☞ Important Note
+I am far from happy to the actual result. There are a lot of biases, for example in the choice of the parameters. The same can be said about the data I gathered. I am using very imprecise informations. But, as far as I know, this is the only article which use many different parameters to help you choose a web framework.
Here are the important features (properties/parameters) I selected to make the choice:
+
+
Popularity, which correlate with:
+
+
number of tested libraries
+
facility to find learning material
+
ability to find another developer to work with
+
+
Efficiency, which is generally correlated to:
+
+
how much processing power you’ll need per user
+
maintenance price per user
+
how long the user will wait to see/update data
+
+
Expressiveness, which is generally correlated to:
+
+
faster development
+
flexibility, adaptability
+
+
Robustness, which correlate with:
+
+
security
+
fewer bugs
+
+
+
Each feature is quite important and mostly independant from each other. I tried to embrace most important topics concerning web frameworks with these four properties. I am fully concious some people might lack another important feature. Nonetheless the methodology used here can be easily replicated. If you lack an important property add it at will and use this choice method.
+
Also each feature is very hard to measure with precision. This is why we will only focus on order of magnitude.
+
For each property a framework could have one of the six possible values: Excellent, Very Good, Good, Medium, Bad or Very Bad
+
So how to make a decision model from these informations?
+
One of the most versatile method is to give a weight for each cluster value. And to select the framework maximizing this score:
We don’t make any difference between excellent and very good in popularity.
+
Concerning efficient framework in excellent cluster will have 2 more points than the “very good” cluster.
+
+
So for each framework we compute its score relatively to a weighted table. And we select the best(s).
+
Example: Using this hypothetic framework and the preceeding table.
+
+
+
+
+
Expressiveness
+
Popularity
+
Efficiency
+
Robustness
+
+
+
+
+
yog
+
Excellent
+
Very Bad
+
Medium
+
Very Good
+
+
+
+
score(yog) = 10 + 0 + 4 + 8 = 22
+
+
Most needs should be expressed by such a weighted table. In the result section, we will discuss this further.
+
It is now time to try to get these measures.
+
Objective measures
+
None of the four properties I choosen can be measured with perfect precision. But we could get the order of magnitude for each.
+
I tried to focus on the framework only. But it is often easier to start by studying the language first.
+
For example, I have datas about popularity by language and I also have different datas concerning popularity by framework. Even if I use only the framework focused datas in my final decision model, it seemed important to me to discuss about the datas for the languages. The goal is to provide a tool to help decision not to give a decision for you.
+
Popularity
+
RedMonk Programming Language Rankings (January 2013) provide an apparent good measure of popularity. While not perfect the current measure feel mostly right. They create an image using stack overflow and github data. Vertical correspond to the number of questions on stackoverflow. Horizontal correspond to the number of projects on github.
+
If you look at the image, your eye can see about four clusters. The 1st cluster correspond to mainstream languages:
+
+
Most developer know at least one of these language.
+
The second cluster is quite bigger. It seems to correspond to languages with a solid community behind them.
+
+
I don’t get into detail, but you could also see third and fourth tier popular languages.
I don’t thing I could find easily web frameworks for third or fourth tier languages.
+
For now, I only talked about language popularity. But what about framework popularity? I made a test using number of question on stackoverflow only. Then by dividing by two for each 6 cluster:
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
%
+
+
+
+
+
Excellent
+
Ruby
+
Rails
+
176208
+
100%
+
+
+
Very Good
+
Python
+
Django
+
57385
+
<50%
+
+
+
+
Java
+
Servlet
+
54139
+
+
+
+
+
Java
+
Spring
+
31641
+
+
+
+
+
Node.js
+
node.js
+
27243
+
+
+
+
+
PHP
+
Codeigniter
+
21503
+
+
+
+
+
Groovy
+
Grails
+
20222
+
+
+
+
Good
+
Ruby
+
Sinatra
+
8631
+
<25%
+
+
+
+
Python
+
Flask
+
7062
+
+
+
+
+
PHP
+
Laravel
+
6982
+
+
+
+
+
PHP
+
Kohana
+
5959
+
+
+
+
+
Node.js
+
Express
+
5009
+
+
+
+
Medium
+
PHP
+
Cake
+
4554
+
<13%
+
+
+
+
C♯
+
ServiceStack
+
3838
+
+
+
+
+
Scala
+
Play
+
3823
+
+
+
+
+
Java
+
Wicket
+
3819
+
+
+
+
+
Dart
+
Dart
+
3753
+
+
+
+
+
PHP
+
Slim
+
3361
+
+
+
+
+
Python
+
Tornado
+
3321
+
+
+
+
+
Scala
+
Lift
+
2844
+
+
+
+
+
Go
+
Go
+
2689
+
+
+
+
Bad
+
Java
+
Tapestry
+
1197
+
<6%
+
+
+
+
C♯
+
aspnet
+
1000
+
+
+
+
+
Haskell
+
Yesod
+
889
+
+
+
+
+
PHP
+
Silex
+
750
+
+
+
+
+
PHP
+
Lithium
+
732
+
+
+
+
+
C♯
+
nancy
+
705
+
+
+
+
Very bad
+
Java
+
Grizzly
+
622
+
<3%
+
+
+
+
Erlang
+
Cowboy
+
568
+
+
+
+
+
Perl
+
Dancer
+
496
+
+
+
+
+
PHP
+
Symphony2
+
491
+
+
+
+
+
Go
+
Revel
+
459
+
+
+
+
+
Clojure
+
Compojure
+
391
+
+
+
+
+
Perl
+
Mojolicious
+
376
+
+
+
+
+
Scala
+
Scalatra
+
349
+
+
+
+
+
Scala
+
Finagle
+
336
+
+
+
+
+
PHP
+
Phalcon
+
299
+
+
+
+
+
js
+
Ringo
+
299
+
+
+
+
+
Java
+
Gemini
+
276
+
+
+
+
+
Haskell
+
Snap
+
263
+
+
+
+
+
Perl
+
Plack
+
257
+
+
+
+
+
Erlang
+
Elli
+
230
+
+
+
+
+
Java
+
Dropwizard
+
188
+
+
+
+
+
PHP
+
Yaf
+
146
+
+
+
+
+
Java
+
Play1
+
133
+
+
+
+
+
Node.js
+
Hapi
+
131
+
+
+
+
+
Java
+
Vertx
+
60
+
+
+
+
+
Scala
+
Unfiltered
+
42
+
+
+
+
+
C
+
onion
+
18
+
+
+
+
+
Clojure
+
http-kit
+
17
+
+
+
+
+
Perl
+
Kelp
+
16
+
+
+
+
+
PHP
+
Micromvc
+
13
+
+
+
+
+
Lua
+
Openresty
+
8
+
+
+
+
+
C++
+
cpoll-cppsp
+
5
+
+
+
+
+
Clojure
+
Luminus
+
3
+
+
+
+
+
PHP
+
Phreeze
+
1
+
+
+
+
+
As we can see, our framework popularity indicator can be quite different from its language popularity. For now I didn’t found a nice way to merge the results from RedMonk with these one. So I’ll use these unperfect one. Hopefully the order of magninute is mostly correct for most framework.
+
Efficiency
+
Another objective measure is efficiency. We all know benchmarks are all flawed. But they are the only indicators concerning efficiency we have.
+
I used the benchmark from benchmarksgame. Mainly, there are five clusters:
+
+
+
+
1x→2x
+
C, C++
+
+
+
2x→3x
+
Java 7, Scala, OCamL, Haskell, Go, Common LISP
+
+
+
3x→10x
+
C♯, Clojure, Racket, Dart
+
+
+
10x→30x
+
Erlang
+
+
+
30x→
+
PHP, Python, Perl, Ruby, JRuby
+
+
+
+
Remarks concerning some very slow languages:
+
+
PHP ; huge variations, can be about 1.5x C speed in best case.
+
Python ; huge variations, can be about 1.5x C speed in best case
+
Perl ; Can be about 3x C speed in best case
+
Ruby, JRuby ; mostly very slow.
+
+
This is a first approach. The speed of the language for basic benchmarks. But, here we are interrested in web programming. Fortunately techempower has made some tests focused on most web frameworks:
These benchmark doesn’t fit well with our needs. The values are certainly quite imprecise to your real usage. The goal is just to get an order of magnitude for each framework. Another problem is the high number of informations.
+
As always, we should remember these informations are also imprecise. So I simply made some classes of efficiency.
+
Remark: I separated the clusters by using power of 2 relatively to the fastest.
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
slowness
+
+
+
+
+
Excellent
+
C++
+
cpoll-cppsp
+
114,711
+
1×
+
+
+
+
Jav
+
gemini
+
105,204
+
+
+
+
+
Lua
+
openresty
+
93,882
+
+
+
+
+
Jav
+
servlet
+
90,580
+
+
+
+
+
C++
+
cpoll-pool
+
89,167
+
+
+
+
+
Go
+
go
+
76,024
+
+
+
+
+
Sca
+
finagle
+
68,413
+
+
+
+
+
Go
+
revel
+
66,990
+
+
+
+
+
Jav
+
rest-express
+
63,209
+
+
+
+
Very Good
+
Jav
+
wicket
+
48,772
+
>2×
+
+
+
+
Sca
+
scalatra
+
48,594
+
+
+
+
+
Clj
+
http-kit
+
42,703
+
+
+
+
+
Jav
+
spring
+
36,643
+
>3×
+
+
+
+
PHP
+
php
+
36,605
+
+
+
+
+
Jav
+
tapestry
+
35,032
+
+
+
+
+
Clj
+
compojure
+
32,088
+
+
+
+
+
JS
+
ringo
+
31,962
+
+
+
+
+
Jav
+
dropwizard
+
31,514
+
+
+
+
+
Clj
+
luminus
+
30,672
+
+
+
+
Good
+
Sca
+
play-slick
+
29,950
+
>4×
+
+
+
+
Sca
+
unfiltered
+
29,782
+
+
+
+
+
Erl
+
elli
+
28,862
+
+
+
+
+
Jav
+
vertx
+
28,075
+
+
+
+
+
JS
+
nodejs
+
27,598
+
+
+
+
+
Erl
+
cowboy
+
24,669
+
+
+
+
+
C
+
onion
+
23,649
+
+
+
+
+
Hkl
+
yesod
+
23,304
+
+
+
+
+
JS
+
express
+
22,856
+
>5×
+
+
+
+
Sca
+
play-scala
+
22,372
+
+
+
+
+
Jav g
+
rizzly-jersey
+
20,550
+
+
+
+
+
Py
+
tornado
+
20,372
+
>6×
+
+
+
+
PHP
+
phalcon
+
18,481
+
+
+
+
+
Grv
+
grails
+
18,467
+
+
+
+
+
Prl
+
plack
+
16,647
+
>7×
+
+
+
+
PHP
+
yaf
+
14,388
+
+
+
+
Medium
+
JS
+
hapi
+
11,235
+
>10×
+
+
+
+
Jav
+
play1
+
9,979
+
+
+
+
+
Hkl
+
snap
+
9,196
+
+
+
+
+
Prl
+
kelp
+
8,250
+
+
+
+
+
Py
+
flask
+
8,167
+
+
+
+
+
Jav
+
play-java
+
7,905
+
+
+
+
+
Jav p
+
lay-java-jpa
+
7,846
+
+
+
+
+
PHP
+
micromvc
+
7,387
+
+
+
+
+
Prl
+
dancer
+
5,040
+
>20×
+
+
+
+
Prl
+
mojolicious
+
4,371
+
+
+
+
+
JS
+
ringo-conv
+
4,249
+
+
+
+
+
Py
+
django
+
4,026
+
+
+
+
+
PHP
+
codeigniter
+
3,809
+
>30×
+
+
+
Bad
+
Rby
+
rails
+
3,445
+
+
+
+
+
Sca
+
lift
+
3,311
+
+
+
+
+
PHP
+
slim
+
3,112
+
+
+
+
+
PHP
+
kohana
+
2,378
+
>40×
+
+
+
+
PHP
+
silex
+
2,364
+
+
+
+
Very Bad
+
PHP
+
laravel
+
1,639
+
>60×
+
+
+
+
PHP
+
phreeze
+
1,410
+
+
+
+
+
PHP
+
lithium
+
1,410
+
+
+
+
+
PHP
+
fuel
+
1,410
+
+
+
+
+
PHP
+
cake
+
1,287
+
>80×
+
+
+
+
PHP
+
symfony2
+
879
+
>100×
+
+
+
+
C#
+
aspnet-mvc
+
871
+
+
+
+
+
Rby
+
sinatra
+
561
+
>200×
+
+
+
+
C#
+
servicestack
+
51
+
+
+
+
+
Dar
+
dart
+
0
+
+
+
+
+
C#
+
nancy
+
0
+
+
+
+
+
Prl
+
web-simple
+
0
+
+
+
+
+
These are manually made clusters. But you get the idea. Certainly, some framework could jump between two different clusters. So this is something to remember. But as always, the order of magnitude is certainly mostly right.
+
Expressiveness
+
Now, how to objectively measure expressiveness?
+
RedMonk had a very good idea to find an objective (while imprecise) measure of each language expressiveness. Read this article for details.
+
After filtering languages suitable for web development, we end up with some clusters:
+
+
+
+
Cluster
+
Languages
+
+
+
+
+
Excellent
+
Coffeescript, Clojure, Haskell
+
+
+
Very Good
+
Racket, Groovy, R, Scala, OCamL, F♯, Erlang, Lisp, Go
+
+
+
Medium
+
Perl, Python, Objective-C, Scheme, Tcl, Ruby
+
+
+
Bad
+
Lua, Fortran (free-format), PHP, Java, C++, C♯
+
+
+
Very Bad
+
Assembly, C, Javascript,
+
+
+
+
Unfortunately there is no information about dart. So I simply give a very fast look at the syntax. As it looked a lot like javascript and js is quite low. I decided to put it close to java.
+
Also an important remark, javascript score very badly here while coffeescript (compiling to js) score “excellent”. So if you intend to use a javascript framework but only with coffescript that should change substantially the score. As I don’t believe it is the standard. Javascript oriented framework score very badly regarding expressiveness.
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Grv
+
grails
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Medium
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Bad
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
Very Bad
+
C
+
onion
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
+
Robustness
+
I couldn’t find any complete study to give the number of bug relatively to each framework/language.
+
But one thing I saw from experience is the more powerful the type system the safest your application is. While the type system doesn’t remove completely the need to test your application a very good type system tend to remove complete classes of bug.
+
Typically, not using pointer help to reduce the number of bugs due to bad references. Also, using a garbage collector, reduce greatly the probability to access unallocated space.
+
+
From my point of view, robustness is mostly identical to safety.
+
Here are the clusters:
+
+
+
+
Excellent
+
Haskell, Scheme, Erlang
+
+
+
Very Good
+
Scala, Java, Clojure
+
+
+
Good
+
Ruby, Python, Groovy, javascript, PHP
+
+
+
Medium
+
C++, C#, Perl, Objective-C, Go, C
+
+
+
+
So applying this to frameworks gives the following clusters:
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Good
+
Grv
+
grails
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Medium
+
C
+
onion
+
+
+
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
+
The result
+
For the result I initialized the table with my own needs.
+
And I am quite happy it confirms my current choice. I sware I didn’t given yesod any bonus point. I tried to be the most objective and factual as possible.
+
Now, it is up to you to enter your preferences.
+
On each line you could change how important a feature is for you. From essential to unsignificant. Of course you could change the matrix at will.
+
I just show a top 10 frameworks. In order to give a more understandable measure I provide the log of the score.
+
+
+
+
+
+Excellent
+
+
+Very good
+
+
+Good
+
+
+Medium
+
+
+Bad
+
+
+Very bad
+
+
+Importance
+
+
+
+
+Expressiveness
+
+
+
+
+Popularity
+
+
+
+
+Efficiency
+
+
+
+
+Robustness
+
+
+
+
+Click to force refresh
+
+
+
+
+
+
+
I didn’t had the courage in explaining in what the scoring system is good. Mostly, if you use product instead of sum for the score you could use power of e for the values in the matrix. And you could see the matrix as a probability matrix (each line sum to 1). Which provide a slighly better intuition on whats going on.
+
Remember only that values are exponential. Do not double an already big value for example the effect would be extreme.
+
Conclusion
+
All of this is based as most as I could on objective data. The choice method seems both rather rational and classical. It is now up to you to edit the score matrix to set your needs.
+
I know that in the current state there are many flaws. But it is a first system to help make a choice rationally.
+
I encourage you to go further if you are not satisfied by my method.
+
The source code for the matrix shouldn’t be too hard to read. Just read the source of this webpage. You could change the positionning of some frameworks if you believe I made some mistake by placing them in some bad clusters.
+
So I hope this tool will help you in making your life easier.
tlpl: Utiliser m4 pour accroître le pouvoir d’xslt et d’svg. Example cool, les fractales.
+
+
Lorsqu’xml fût inventé beaucoup pensaient que c’était l’avenir. Passer de fichiers plat à des fichiers structurés standardisés fût un grand progrès dans beaucoup de domaines. Cerain se mirent à voir du xml de partout. À tel point que les les format compatibles xml naquirent de toute part. Non seulement comme format de fichier, mais aussi comme format pour un langage de programmation.
+
Ô joie !
+
Malheureusement, xml fût fabriquer pour le transfert de données. Pas du tout pour être vu ou édité directement. La triste vérité est qu’xml est verbeux et laid. Dans un monde parfait, nous ne devrions avoir des programmes qui s’occupent de nous afficher correctement le xml pour nous épargner la peine de les voir directement. Mais devinez quoi ? Notre monde n’est pas parfait. Beaucoup de programmeurs sont ainsi forcé de travailler directement avec de l’xml.
+
xml, n’est pas le seul cas de format mal utilisé que je connaisse. Vous avez d’autres formats dans lesquels il serait très agréable d’ajouter des variables, des boucles, des fonctions…
+
Mais je suis là pour vous aider. Si comme moi vous détestez xslt ou écrire de l’xml. Je vais vous montrer une façon d’améliorer tout ça.
+
Un exemple avec xslt
+
Commençons avec le pire cas de langage xml que je connaisse : xslt. Tous les développeurs qui ont déjà dû écrire du xslt savent à quel point ce langage est horrible.
+
Pour réduire la “verbosité” de tels langages, il y a un moyen. m4. Oui, le préprocesseur utilisé par C et C++.
+
Voici certains exemples :
+
+
Les variables, au lieu d’écrire myvar = value, voici la version xslt :
Et vous pouvez profitez ! Maintenant xslt devient plus lisible et plus facile à éditer.
+
La partie la plus cool: les fractales !
+
À ses débuts, beaucoup pensaient que ce serait le nouveau Flash. Apparemment, ce devrait plutôt être canvas avec du javascript qui occupera cette place.
+
Tout d’abord, laissez moi vous montrer le résultat :
Le positionnement du texte “esod” par rapport au “λ” renversé a été en jouant avec firebug. De cette façon je n’avais pas à regénérer pour tester.
+
Faire une telle fractale revient à :
+
+
Choisir un élément racine ;
+
le dupliquer et le transformer ;
+
le résultat est un nouveau sous-élément ;
+
répéter à partir de 2 mais en utilisant le sous-élément comme nouvelle racine.
+
Arréter lorsque la récursion est assez profonde.
+
+
Si j’avais dû faire ça manuellement, il m’aurait fallu faire beaucoup de copier/coller dans mon svg. Simplement parce que la transformation est toujours la même, mais je ne pouvais pas dire, utiliser la transformation appelée “titi”. Plutôt que copier du xml, j’ai utilisé m4.
Le λ est dupliqué avec trois “transformations” différentes. Les transformations sont : YTRANSFORMONE, YTRANSFORMTWO et YTRANSFORMTHREE.
+
Chaque transformation est une similarité (translation + rotation + zoom, ce qui est équivalent à juste rotation + zoom, mais bon).
+
Une fois fixée chaque transformation peut ensuite être réutilisée pour chaque nouveau niveau.
+
Maintenant YTRANSCOMPLETE entre en jeu. Cette macro prend deux arguments. Le niveau courant et le niveau précédent. Cette macro va dupliquer le niveau précédent en lui appliquant chacune des 3 transformations. Au niveau 0, le contenu est un seul grand λ, le niveau 1 en contient 3. Le niveau 2 en contient 9, etc… Le niveau 5 contient 35=243 λ. Tous les niveaux combinés représentent 36-1 / 2 = 364 λ.
+
L’avantage principal c’est que je pouvais visualiser le résultat final facilement. Sans ce système de macro, pour faire une preview il m’aurait fallu faire des copier/coller + quelques modifications à chaque essai.
+
Conclusion
+
Ce fut très amusant de faire une fractale en svg, mais la partie la plus intéressante était d’augmenter la puissance d’expressivité du langage en utilise un préprocesseur. J’ai utilisé cette méthode avec xslt pour une vrai application par exemple. On peut aussi utiliser m4 pour faire des includes d’autres fichiers. Typiquement je l’ai utiliser pour les includes dans un format obscur. Mais vous pouvez aussi le considérer pour des includes dans du HTML. Par exemple pour fabriquer un site statique rapidement, m4 peut se révéler utile pour inclure un footer ou un menu sur toutes les pages par exemple. J’ai aussi pensé que l’on pouvait utiliser m4 pour structurer des programmes comme brainfuck.
Si vous êtes sous windows, téléchargez Haskell Platform et suivez les instructions pour utiliser Haskell LTS.
+
Si vous voulez savoir le pourquoi et le comment ; lisez le reste de l’article.
+
+
Pourquoi ?
+
La plus grande faiblesse d’Haskell n’a rien à voir avec le langage en lui-même mais avec son écosystème.
+
The main problem I’ll try to address is the one known as cabal hell. The community is really active in fixing the issue. I am very confident that in less than a year this problem will be one of the past. But to work today, I provide an install method that should reduce greatly two effects of cabal hell:
+
+
dependency error
+
lost time in compilation (poor polar bears)
+
+
With my actual installation method, you should minimize your headache and almost never hit a dependency error. But there could exists some. If you encounter any dependency error, ask gently to the package manager to port its package to stackage.
+
So to install copy/paste the following three lines in your terminal:
You can read the script and you will see that this is quite straightforward.
+
+
It downloads the latest GHC binary for you system and install it.
+
It does the same with the cabal program.
+
It updates your cabal config file to use Haskell LTS.
+
It enable profiling to libraries and executables.
+
It installs some useful binaries that might cause compilation error if not present.
+
+
As the version of libraries is fixed up until you update the Haskell LTS version, you should never use cabal sandbox. That way, you will only compile each needed library once. The compiled objects/binaries will be in your ~/.cabal directory.
+
Some Last Words
+
This script use the latest Haskell LTS. So if you use this script at different dates, the Haskell LTS might have changed.
+
While it comes to cabal hell, some solutions are sandboxes and nix. Unfortunately, sandboxes didn’t worked good enough for me after some time. Furthermore, sandboxes forces you to re-compile everything by project. If you have three yesod projects for example it means a lot of time and CPU. Also, nix didn’t worked as expected on OS X. So fixing the list of package to a stable list of them seems to me the best pragmatic way to handle the problem today.
+
From my point of view, Haskell LTS is the best step in the right direction. The actual cabal hell problem is more a human problem than a tool problem. This is a bias in most programmer to prefer resolve social issues using tools. There is nothing wrong with hackage and cabal. But for a package manager to work in a static typing language as Haskell, packages must work all together. This is a great strength of static typed languages that they ensure that a big part of the API between packages are compatible. But this make the job of package managing far more difficult than in dynamic languages.
+
People tend not to respect the rules in package numbering1. They break their API all the time. So we need a way to organize all of that. And this is precisely what Haskell LTS provide. A set of stable packages working all together. So if a developer break its API, it won’t work anymore in stackage. And whether the developer fix its package or all other packages upgrade their usage. During this time, Haskell LTS end-users will be able to develop without dependency issues.
tlpl: Les boutons des réseaux sociaux traquent vos utilisateurs, ont un design incohérent avec celui de votre site, utilisent des ressources, ralentissent le rendu de vos pages.
+
Faite les choses bien. Utilisez des liens statiques.
+
Si vous n’avez pas envie de lire, copiez et collez simplement le code suivant dans votre html :
Ever been on a website and want to tweet about it? Fortunately, the website might have a button to help you. But do you really know what this button do?
+
The “Like”, “Tweet” and “+1” buttons will call a javascript. It will get access to your cookies. It helps the provider of the button to know who you are.
+
In plain English, the “+1” button will inform Google you are visiting the website, even if you don’t click on “+1”. The same is true for the “like” button for facebook and the “tweet this” button for twitter.
+
The problem is not only a privacy issue. In fact (sadly imho) this isn’t an issue for most people. These button consume computer ressources. Far more than a simple link. It thus slow down a bit the computer and consume energy. These button could also slow down the rendering of your web page.
+
Another aspect is their design. Their look and feel is mostly imposed by the provider.
+
The most problematic aspect in my opinion is to use a third party js on your website. What if tomorrow twitter update their tweet button? If the upgrade break something for only a minority of people, they won’t fix it. This could occur anytime without any notification. They just have to add a document.write in their js you call asynchronously and BAM! Your website is just an empty blank page. And as you call many external ressources, it can be very difficult to find the origin of the problem.
+
Using social network buttons:
+
+
Pros:
+
+
help user share your website,
+
can provide a popularity indicator to your users.
+
+
Cons:
+
+
you help tracking your users,
+
generally doesn’t follow the design of your website,
+
use more computer ressources,
+
slow down your website,
+
executing third party js can break things silently.
+
+
+
Solutions
+
I will provide you two solutions with the following properties:
+
+
Pros:
+
+
help user share your website,
+
doesn’t follow your user,
+
use almost no computer ressource,
+
doesn’t slow down your website,
+
doesn’t execute any third party js on your website.
You use less computer ressources and more generally power ressources which is good for the planet,
+
Your web pages will load faster.
+
+
ps: On my personal website I continue to use Google analytics. Therefore, Google (and only Google, not facebook nor twitter) can track you here. But I might change this in the future.
tlpl: La typography sur le web est pourrie et nous ne somme pas près de voir ce problème réparé.
+
+
Je suis tombé sur ce site: open typography. Leur message principal est :
+
+
«There is no reason to wait for browser development to catch up. We can all create better web typography ourselves, today.»
+
+
ou en français :
+
+
«Nous ne somme pas obligé d’attendre le développement des navigateurs. Nous pouvons créer un web avec une meilleure typographie aujourd’hui.»
+
+
Comme quelqu’un qui a déjà essayé d’améliorer la typographie de son site web, et en particulier des ligatures, je crois que c’est faux.
+
J’ai déjà écrit un système automatique qui détecte et ajoute des ligatures en utilisant des caractères unicode. Cependant je n’ai jamais publié cette amélioration sur le web et voilà pourquoi :
+
Tout d’abord, qu’est-ce qu’un ligature ?
+
+
+
+
Quel est le problème des ligatures sur le web ? Le premier c’est que vous ne pouvez pas chercher les mots qui contiennent ces ligatures. Par exemple essayez de chercher le mot “first”.
first ← Une jolie ligature, mais introuvable avec une recherche (C-f).
+
+
Le second problème est le rendu. Par exemple, essayer d’utiliser un charactère de ligature en petites capitales :
+
+
first
+
first
+
+
Voici une capture d’écran pour que vous voyez ce que je vois :
+
+
+
+
Le navigateur est incapable de comprendre que le caractère de ligature “fi” doit être rendu comme fi lorsqu’il est en petites capitales. Et une part du problème est que l’on peut décider d’écrire en petite majuscule dans le css.
+
Comment par exemple utiliser un charactère de ligature unicode sur un site qui possède différents rendus via différentes css ?
+
Comparons à LaTeX
+
+
+
+
Si vous faites attention au détail, vous constaterez que le premier “first” contient une ligature. Bien entendu la deuxième ligne est affichée correctement. Le code que j’ai utilisé pour avoir ce rendu est simplement :
Clairement il sera difficile aux navigateurs de corriger ces problèmes. Imaginez le nombre de petites exceptions.
+
+
Le texte est en petites capitales, je ne dois pas utiliser de ligatures.
+
Le mot courant contient un caractère de ligature, je ne dois pas chercher d’autre ligature dans ce mot.
+
La fonte n’a pas défini de caractère unicode pour la ligature, je ne dois pas l’utiliser.
+
Une commande javascript a modifé le CSS, je dois vérifier si je dois remplacer les ligatures par les deux caractères.
+
etc…
+
+
Dans tous les cas, si quelqu’un possède une solution je suis preneur !
+
+
+
+
En réalité, vous devriez pouvoir voir une ligature. Maintenant j’utilise : text-rendering: optimizelegibility. Le rendu est correct parce que j’utilise une fonte correct, à savoir Computer Modern de Donald Knuth.↩
tlpl: Comment utiliser vim comme une IDE très efficace
+
In Learn Vim Progressively I’ve show how Vim is great for editing text, and navigating in the same file (buffer). In this short article you’ll see how I use Vim as an IDE. Mainly by using some great plugins.
+
+
Vim Plugin Manager
+
There are a lot of Vim plugins. To manage them I use vim-plug.
☞ Note I have two parts in my .vimrc. The first part contains the list of all my plugins. The second part contains the personal preferences I setted for each plugin. I’ll separate each part by ... in the code.
+
+
Survival
+
Colorscheme
+
+
+
+
Before anything, you should protect your eyes using a readable and low contrast colorscheme.
+
For this I use solarized dark. To add it, you only have to write this in your ~/.vimrc file:
You should be able to see and destroy trailing whitespaces.
+
+
+
+
Plug 'bronson/vim-trailing-whitespace'
+
You can clean trailing whitespace with :FixWhitespace.
+
And also you should see your 80th column.
+
if (exists('+colorcolumn'))
+ set colorcolumn=80
+ highlight ColorColumn ctermbg=9
+endif
+
+
+
+
File Management
+
One of the most important hidden skills in programming is the ability to search and find files in your projects.
+
The majority of people use something like NERDTree. This is the classical left column with a tree of files of your project. I stopped to use this. And you should probably too.
+
I switched to unite. No left column lost. Faster to find files. Mainly it works like Spotlight on OS X.
+
First install ag (the silver search). If you don’t know ack or ag your life is going to be upgraded. This is a simple but essential tool. It is mostly a grep on steroids.
+
" Unite
+" depend on vimproc
+" ------------- VERY IMPORTANT ------------
+" you have to go to .vim/plugin/vimproc.vim and do a ./make
+" -----------------------------------------
+Plug 'Shougo/vimproc.vim'
+Plug 'Shougo/unite.vim'
+
+...
+
+let g:unite_source_history_yank_enable = 1
+try
+ let g:unite_source_rec_async_command='ag --nocolor --nogroup -g ""'
+ call unite#filters#matcher_default#use(['matcher_fuzzy'])
+catch
+endtry
+" search a file in the filetree
+nnoremap <space><space> :split<cr> :<C-u>Unite -start-insert file_rec/async<cr>
+" reset not it is <C-l> normally
+:nnoremap <space>r <Plug>(unite_restart)
+
Now type space twice. A list of files appears. Start to type some letters of the file you are searching for. Select it, type return and bingo the file opens in a new horizontal split.
+
+
+
+
If something goes wrong just type <space>r to reset the unite cache.
+
Now you are able to search file by name easily and efficiently.
+
Now search text in many files. For this you use ag:
+
Plug 'rking/ag.vim'
+...
+" --- type ° to search the word in all files in the current dir
+nmap ° :Ag <c-r>=expand("<cword>")<cr><cr>
+nnoremap <space>/ :Ag
+
Don’t forget to add a space after the :Ag.
+
These are two of the most powerful shortcut for working in a project. using ° which is nicely positioned on my azerty keyboard. You should use a key close to *.
+
So what ° is doing? It reads the string under the cursor and search for it in all files. Really useful to search where a function is used.
+
If you type <space>/ followed by a string, it will search for all occurrences of this string in the project files.
+
So with this you should already be able to navigate between files very easily.
+
Language Agnostic Plugins
+
Git
+
+
+
+
Show which line changed since your last commit.
+
Plug 'airblade/vim-gitgutter'
+
And the “defacto” git plugin:
+
Plug 'tpope/vim-fugitive'
+
You can reset your changes from the latest git commit with :Gread. You can stage your changes with :Gwrite.
Just select and type Return then space. Type Return many type to change the alignments.
+
If you want to align the second column, Return then 2 then space.
+
+
+
+
Basic auto completion: C-n & C-p
+
Vim has a basic auto completion system. The shortcuts are C-n and C-p while you are in insert mode. This is generally good enough in most cases. For example when I open a file not in my configured languages.
+
Haskell
+
My current Haskell programming environment is great!
+
Each time I save a file, I get a comment pointing to my errors or proposing me how to improve my code.
+
So here we go:
+
+
☞ Don’t forget to install ghc-mod with: cabal install ghc-mod
I use - for my leader because I use , a lot for its native usage.
+
+
-ht will highlight and show the type of the block under the cursor.
+
-hT will insert the type of the current block.
+
-hh will unhighlight the selection.
+
+
+
+
+
Clojure
+
+
+
+
My main language at work is Clojure. And my current vim environment is quite good. I lack the automatic integration to lein-kibit thought. If I have the courage I might do it myself one day. But due to the very long startup time of clojure, I doubt I’ll be able to make a useful vim plugin.
+
So mainly you’ll have real rainbow-parenthesis (the default values are broken for solarized).
+
I used the vim paredit plugin before. But it is too restrictive. Now I use sexp which feel more coherent with the spirit of vim.
Working with Clojure will becomre quite smoother. You can eval any part of your code, you must launch a Clojure REPL manually in another terminal thought.
+
Last words
+
I hope it will be useful.
+
Last but not least, if you want to use my vim configuration you can get it here:
Cela fait un moment que je suis la progression du framework yesod. À mon humble avis on peut commencer à l’utiliser pour des applications sérieuses (comprendre en prod). Avant de vous dire pourquoi vous devriez aussi le considérer, je préfère vous parler de bonnes idées (parmi d’autres) introduites par yesod que je n’avais jamais vu ailleurs.
Lorsque vous créez une application web, beaucoup de temps est passé à s’occuper de chaînes de caractères. Des chaînes de caractère pour les URL, le HTML, le Javascript, les CSS, les requêtes SQL, etc… Pour éviter des utilisation malicieuses vous devez protéger chaque chaîne de caractère entre chaque étape. Par exemple supposons que vous entriez comme nom :
Sans une protection correcte, le message “An apple fall” sera affiché à chaque fois que quelqu’un essayera d’accéder au nom de cet utilisateur. Les “types saufs” sont le tonyglandil du web. A chaque chaine de caractère, on lui associe un “type”. A quoi sert cette chaîne de caractère ? Est-ce une URL ? Du javascript ? De l’HTML ? Entre chaque passage d’une représentation à une autre, un transformation is faite par défaut.
+
Yesod fait de son mieux pour typer les objets manipulés et ainsi il fera ce qu’il faut pour ne pas mettre du script dans une URL par exemple.
Les widgets de yesod sont différents des widgets Javascripts (ou java). Pour yesod un widget est un ensemble de morceaux d’appli web. Et si dans une page on veut utiliser plusieurs widgets, alors yesod s’occupe de tout. Des exemples de widgets (au sens yesod) sont :
+
+
Le «footer» d’une page web,
+
Le «header» d’une page web,
+
un bouton qui apparaît lorsque l’on «scrolle» vers le bas,
+
etc…
+
+
Pour chacun de ces widgets vous pourriez avoir besoin d’
+
+
un peu d’HTML,
+
un peu de CSS et
+
un peu de javascript.
+
+
Certain morceau doivent être placés dans le «header» de la page et d’autre dans le «body».
+
Vous pouvez déclarer un widget comme suit (je n’utilise pas la vrai syntaxe) :
De plus, si vous utilisez 10 widgets avec un peu de CSS, yesod fabriquera un unique fichier CSS pour vous. Bien entendu si vous préférez avoir une dizaine de fichier CSS vous pouvez aussi le faire.
+
C’est juste génial !
+
Routage optimisé
+
Dans un système de routage standard (à la ruby on rails par exemple) vous avez pour chaque entrée un couple: regexp → handler
+
La seule façon de découvrir la bonne règle est d’essayer de matcher l’url demandée à chaque expression régulière.
+
Au lieu d’essayer chaque expression régulière, yesod regroupe et compile les routes pour les optimiser. Bien entendu pour pouvoir profiter de cet avantage au mieux, il ne faut pas que deux routes interfèrent entres elles.
Cette définition de route est invalide par défaut dans yesod. Si vous voulez vraiment vous pouvez le faire foncionner quand même, mais il me semble que ça doit être quasiment toujours une mauvaise idée.
et faire le test “est-ce que date = 2003 ?” dans le «handler».
+
Pourquoi yesod?
+
+
La vitesse. Simplement incroyable, je ne pense pas qu’il existe quelque chose de plus rapide aujourd’hui. Regardez d’abord cet article puis celui-ci.
+
Haskell. C’est certainement le langage de programmation le plus difficile à apprendre que j’ai jamais rencontré. Mais aussi l’un des plus incroyables. Si vous voulez rencontrer tout un tas de notions que vous n’avez jamais croisées avant et faire exploser votre cerveau avec de nouvelles idées, alors apprenez Haskell.
+
Bonnes idées et communauté excellente. Cela fait quelques mois que je suis la progression de yesod. Et la vitesse à laquelle tout s’est déroulé est simplement incroyable. De plus les développeurs sont intelligents et super sympa.
+
+
Si vous êtes un “haskeller”, je pense que vous ne devriez pas avoir peur de la syntaxe particulière imposée par la façon standard de faire les choses avec yesod. Il faut essayer un peu plus loin que les premiers tutoriaux du livre.
+
Je pense que yesod va dans la bonne direction d’un web plus sûr et plus rapide. Même si je pense que l’avenir sera que les serveurs devront être limités à faire serveur d’API (JSON ou XML ou n’importe quel autre mode de représentation d’objets).
+
Yesod est juste incroyable. Dépassez les difficultés liées à l’apprentissage d’haskell et essayez le !
Haskell is a high level language and make it harder to shoot you in the foot than C, C++ or Java for example. One of the best property of Haskell being:
+
+
“If your program compile it will be very close to what the programmer intended”.
+
+
Haskell web frameworks handle parallel tasks perfectly. For example even better than node.js3.
+
+
+
+
From the pure technical point of view, Haskell seems to be the perfect web development tool. Weaknesses of Haskell certainly won’t be technical:
+
+
Hard to grasp Haskell
+
Hard to find a Haskell programmer
+
The Haskell community is smaller than the community for /.*/
+
There is not yet an heroku for Haskell. In fact, I use heroku to host my websites but this isn’t straightforward (see the how to).
+
+
I won’t say these are not important drawbacks. But, with Haskell your web application will have both properties to absorb an impressive number of parallel requests securely and to adapt to change.
+
Actually there are three main Haskell web frameworks:
I don’t think there is a real winner between these three framework. The choice I made for yesod is highly subjective. I just lurked a bit and tried some tutorials. I had the feeling yesod make a better job at helping newcomers. Furthermore, apparently the yesod team seems the most active. Of course I might be wrong since it is a matter of feeling.
+
+
+
+
Why did I write this article? The yesod documentation and particularly the book are excellent. But I missed an intermediate tutorial. This tutorial won’t explain all details. I tried to give a step by step of how to start from a five minute tutorial to an almost production ready architecture. Furthermore explaining something to others is a great way to learn. If you are used to Haskell and Yesod, this tutorial won’t learn you much. If you are completely new to Haskell and Yesod it might hopefully helps you. Also if you find yourself too confused by the syntax, it might helps to read this article
+
During this tutorial you’ll install, initialize and configure your first yesod project. Then there is a very minimal 5 minutes yesod tutorial to heat up and verify the awesomeness of yesod. Then we will clean up the 5 minutes tutorial to use some “best practices”. Finally there will be a more standard real world example; a minimal blog system.
Enter your name, choose yosog for the project name and enter Yosog for the name of the Foundation. Finally choose sqlite. Now, start the development cycle:
This will compile the entire project. Be patient it could take a while the first time. Once finished a server is launched and you could visit it by clicking this link:
Up until here, we have a directory containing a bunch of files and a local web server listening the port 3000. If we modify a file inside this directory, yesod should try to recompile as fast as possible the site. Instead of explaining the role of every file, let’s focus only on the important files/directories for this tutorial:
+
+
config/routes
+
Handler/
+
templates/
+
config/models
+
+
Obviously:
+
+
+
+config/routes
+
+
+is where you’ll configure the map url → Code.
+
+
+
+
+Handler/
+
+
+contains the files that will contain the code called when a url is accessed.
+
+
+
+
+templates/
+
+
+contains html, js and css templates.
+
+
+
+
+config/models
+
+
+is where you’ll configure the persistent objects (database tables).
+
+
+
+
During this tutorial we’ll modify other files as well, but we won’t explore them in detail.
+
Also note, shell commands are executed in the root directory of your project instead specified otherwise.
+
We are now ready to start!
+
Echo
+
To verify the quality of the security of the yesod framework, let’s make a minimal echo application.
+
+
Goal:
+
Make a server that when accessed /echo/[some text] should return a web page containing “some text” inside an h1 bloc.
+
+
~/Sites/yosog $ yesod add-handler
+Name of route (without trailing R): Echo
+Enter route pattern (ex: /entry/#EntryId): /echo/#String
+Enter space-separated list of methods (ex: GET POST): GET
+
Almost all work is done for us. The add-handler do the following:
+
Update the config/route file by appending:
+
/echo/#String EchoR GET
+
This line contains three elements: the url pattern, a handler name, an http method.
+
+
create a Handler/Echo.hs file
+
import Handler.Echo in the main Application.hs file
+
declare Handler.Echo in the cabal file for building the application
+
+
Now try to go to localhost:3000/echo/foo. You should get a message explaining getEchoR is not yet implemented.
Don’t worry if you find all of this a bit cryptic. In short it just declare a function named getEchoR with one argument (theText) of type String. When this function is called, it return a Handler RepHtml whatever it is. But mainly this will encapsulate our expected result inside an html text.
+
After saving the file, you should see yesod recompile the application. When the compilation is finished you’ll see the message: Starting devel application.
The special characters are protected for us. A malicious user could not hide some bad script inside.
+
This behavior is a direct consequence of type safety. The url string is put inside a url type. Then the interesting part in the url is put inside a String type. To pass from url type to String type some transformation are made. For example, replace all “%20” by space characters. Then to show the String inside an html document, the string is put inside an html type. Some transformations occurs like replace “<” by “<”. Thanks to yesod, this tedious job is done for us.
Some html (more precisely hamlet) is written directly inside our handler. We should put this part inside another file. Create the new file templates/echo.hamlet containing:
At this point, our web application is nicely structured. We use Data.Text and our views are in templates. It is the time to make a slightly more complex example.
+
Mirror
+
+
+
+
Let’s make another minimal application. You should see a form containing a text field and a validation button. When you enter some text (for example “Jormungad”) and validate, the next page present you the content and its reverse appended to it. In our example it should return “JormungaddagnumroJ”.
+
First, add a new handler:
+
~/Sites/yosog (master) $ yesod add-handler
+Name of route (without trailing R): Mirror
+Enter route pattern (ex: /entry/#EntryId): /mirror
+Enter space-separated list of methods (ex: GET POST): GET POST
+
This time the path /mirror will accept GET and POST requests. Update the corresponding new Handler file (Handler/Mirror.hs):
We will need to use the reverse function provided by Data.Text which explain the additional import.
+
The only new thing here is the line that get the POST parameter named “content”. If you want to know more detail about it and form in general you can take look at the yesod book.
+
Create the two corresponding templates (templates/mirror.hamlet and templates/posted.hamlet):
Also you can try to enter strange values (like <script>alert('Bad');</script>). Like before, your application is quite secure.
+
A Blog
+
We saw how to retrieve http parameters. It is the time to save things into a database.
+
This example will be very minimal:
+
+
GET on /blog should display the list of articles,
+
POST on /blog should create a new article,
+
GET on /blog/<article id> should display the content of the article.
+
+
As before add some handlers
+
~/Sites/yosog (master) $ yesod add-handler
+Name of route (without trailing R): Blog
+Enter route pattern (ex: /entry/#EntryId): /blog
+Enter space-separated list of methods (ex: GET POST): GET POST
+
+~/Sites/yosog (master) $ yesod add-handler
+Name of route (without trailing R): Article
+Enter route pattern (ex: /entry/#EntryId): /blog/#ArticleId
+Enter space-separated list of methods (ex: GET POST): GET
+
Then we declare another model object. Append the following content to config/models:
+
Article
+ title Text
+ content Html
+ deriving
+
As Html is not an instance of Read, Show and Eq, we had to add the deriving line. If you forget it, there will be an error.
+
After the route and the model, we write the handler. Let’s write the content of Handler/Blog.hs. We start by declaring the module and by importing some block necessary to handle Html in forms.
Remark: it is a best practice to add the YesodNic instance inside Foundation.hs. I put this definition here to make things easier but you should see a warning about this orphan instance. To put the include inside Foundation.hs is left as an exercice to the reader.
+
Hint: Do not forget to put YesodNic and nicHtmlField inside the exported objects of the module.
This function defines a form for adding a new article. Don’t pay attention to all the syntax. If you are curious you can take a look at Applicative Functor. You just have to remember areq is for required form input. Its arguments being: areq type label default_value.
You should remark we added some logic inside the template. There is a test and a “loop”.
+
Another very interesting part is the creation of the form. The articleWidget was created by yesod. We have given him the right parameters (input required or optional, labels, default values). And now we have a protected form made for us. But we have to create the submit button.
+
You could take a first look by clicking here. Of course, you could not post something yet.
This function should be used to create a new article. We handle the form response. If there is an error we display an error page. For example if we left some required value blank. If things goes right:
+
+
we add the new article inside the DB (runDB $ insert article)
+
we add a message to be displayed (setMessage $ ...)
The get404 function try to do a get on the DB. If it fails it return a 404 page. The rest should be clear. Here is the content of templates/article.hamlet:
This is the end of this tutorial. I made it very minimal.
+
If you already know Haskell and you want to go further, you should take a look at the recent i18n blog tutorial. It will be obvious I inspired my own tutorial on it. You’ll learn in a very straightforward way how easy it is to use authorizations, Time and internationalization.
+
If, on the other hand you don’t know Haskell. Then you shouldn’t jump directly to web programming. Haskell is a very complex and unusual language. My advice to go as fast as possible in using Haskell for web programming is:
If you have difficulties in understanding concepts like monads, you should really read these articles. For me they were enlightening.
+
If you feel confident, you should be able to follows the yesod book and if you find difficult to follows the yesod book, you should read real world Haskell first.
One can argue these benchmark contains many problems. But the benchmarks are just here to give an order of idea. Mainly Haskell is very fast.↩
+
Generally high level Haskell is slower than C, but low level Haskell is equivalent to C speed. It means that even if you can easily link C code with Haskell, this is not needed to reach the same speed. Furthermore writing a web service in C/C++ seems to be a very bad idea. You can take a look at a discussion on HN about this.↩
Few days ago there were about 20 job offer for Haskell. In only one day! How is that possible? As a real haskeller, I find this situation unbearable!
+
After all, we must avoid success at all cost. And I’ll help SPJ achieve this honorable goal.
+
+
Prevent Interest from beginner
+
Imagine a situation were you see people demonstrating some interest in learning Haskell.
+
Quick! Prevent them from going further.
+
If they come from the dynamic (uni-typed) languages like Python, Javascript…:
+
+
Haskell? A statically typed language??? Hmm… You mean like C and Java?
+
+
Such a remark should immediately shut down any interest in Haskell.
+
If they want to produce application with them:
+
+
Haskell? Isn’t it only a language for student! I don’t think it is useful for REAL WORLD applications!
+
+
If they just want to learn something new:
+
+
Haskell? Ah yes, I remember, mostly they only have the equivalent of Java interfaces and they stopped there. They don’t even have classes!!!! Can you imagine? I don’t even speak about class inheritance.
+
We’re in 2016! And they don’t even support basic Object Oriented Programming. What a joke!
+
+
If they love low level programming:
+
+
Haskell? Ah yes, I heard that lazyness make it impossible to think about code complexity and generally cause lot of space leaks.
+
+
And if it is not enough:
+
+
Haskell? Ah yes. I’m not fan of their Stop the World GC.
+
+
If they come from LISP and the statically typed language remark wasn’t enough. Try to mention the lack of macros in Haskell. Don’t mention template Haskell or even less Generics and all recent progress in GHC.
+
Make it difficult to install
+
Many hints there:
+
+
Send them on another compiler than GHC
+
Explain that they should never use a binary distribution of GHC! And they must compile it manually! It might not stop them but it will make the installation process much more tedious.
+
Lie! Explain there is a severe security issue with latest tools. Explain they must use cabal-install 1.18 or older.
+
Also explain them that in order to be able to handle lib dependencies correctly they MUST first learn Nix! Never talk about stack, cabal freeze, … While Nix is great, forcing new user completely alien to all these concepts to first learn it before starting to write their first line of code can greatly reduce their enthusiasm. Bonus point if you make them believe you can only program in Haskell on NixOS.
+
+
Make it difficult to learn
+
Make new comers feel dumb
+
The very first thing to do is to explain how Haskell is so easy to learn. How natural it is for everybody you know. And except someone you always considered very dumb, everybody was very productive in Haskell in few hours.
+
Use vocabulary alien to them as much as possible. Here is a list of terms you should use in the very first minutes of your description of Haskell:
+
+
catamorphism (bonus if you mention that the word come from the Greek κατα for catastrophe, that way you’ll look like a snob and you also use the word catastrophe in a Haskell context).
+
Monad! Of course you should use it ASAP! And explain they are exactly like potatoes or bananas shaped chairs. Double bonus if you explain that monad are really simple as they are just a monoid in the category of endofunctors.
+
GADTs
+
Yoneda Lemma
+
Homotopy Type Theory
+
…
+
+
Each of this term will hopefully be intimidating.
+
Tutorial authors
+
Please don’t provide an obvious first example like:
This nice example should overflow the number of new concepts a Haskell newcomer should deal with:
+
+
Language extensions. Each extension can take a lot of time to be explained.
+
Strange notations:
+
+
:<|>
+
'[] instead of []
+
+
Proxy
+
Immediate usage of $
+
deriving ha ha! You’ll need to explain typeclasses first!
+
the definition for getItemById
+
+
Of course use the most of your energy explaining the language extensions first. Use a great deal of details and if possible use as much as possible references to Category Theory. You’ll get bonus points if you mention HoTT! Double bonus points if you explain that understanding all details in HoTT is essential to use Haskell on a daily basis.
+
Explain that what this does is incredible but for the wrong reasons. For example don’t mention why instance ToJSON Item is great. But insist that we achieved to serve a JSON with extreme elegance and simplicity. Keep insisting on the simplicity and forgot to mention type safety which is one of the main benefit of Servant.
+
If you’re afraid that this example might be too close to a real world product, you can simply use some advanced lenses examples:
Certainly a great example to start a new language with.
+
Library authors
+
+
Do your best not to respect versioning number policy to maximize the probability to break things.
+
Don’t write any documentation, type are enough!
+
Even better, add mistakes to your documentation
+
Each time you can use a meaningful notation, make it wrong. For example, if you have a symmetric relation use an asymmetric symbol to represent it.
+
If possible remove all function names and only use symbols of at least 5 letters: For example you can replace your function log :: Level -> String -> IO () by (<=.=$$.).
+
+
If the the trends continue toward growth, then we might need to go further at the risk of breaking our own ecosystem:
+
+
Split your libs as much as possible. The best would be to use one lib by symbol
+
Use unsafePerformIO as much as possible
+
Push to Hackage a version not accessible on your public repository
+
modify the package on Hackage using the same version but with incompatible API
+
Add memory leaks
+
Add bugs
+
Add back doors and publish how to use them
+
+
Yes we said, at all cost!
+
Conclusion & Mistake
+
So with all of this I believe we should be on the right track to avoid success at all cost!
+
Sorry? What?
+
Oh… Apparently I made a precedence mistake!
+
SPJ didn’t asked to avoid success $ at all cost but to avoid $ success at all cost1.
+
Sorry! My bad! Forget about all of this. Keep the good work everybody! Haskell is certainly one of the most awesome language in the world! Its community is also just great.
+
I’m really happy to see it growth every year. Thanks to all contributors making it possible to still have a lot of fun after many years using Haskell!
+
And the fact that in Haskell the right choice is preferred to the easiest choice, certainly helped.
tl;dr: Some hints on how to make great documentation for Haskell libraries.
+
+
Create a Tutorial module containing nothing except documentation.
+
Mention the Tutorial module in your cabal description
+
Use doctest to check your documentation is up to date
+
For more complex real world examples, link to the source of some test.
+
+
+
Great documentation make a big difference. A bad documentation could simply make people not using your lib.
+
My friend was learning Haskell. To start he tried a Haskell library to make a small application. The documentation was deprecated to the point he wasn’t able to make a basic example work. How do you believe he felt? What does he thought about Haskell in general?
+
So here are my hint on how to make a great documentation in Haskell.
+
Documentation can take many different form.
+
+
Tutorials/Guides – write some prose which friendly take a user by hand and help him
+
Examples – how to use each function
+
Generated API Documentation – haddock
+
+
Hints
+
Tutorials/Guides
+
+
Create a new module named Tutorial (or Guide.GuideTopic)
+
Create a link to the tutorial in the cabal description
To prevent obsolescence of your tutorial, use doctest.
+
That way when you’ll do a stack test or cabal test you’ll get errors if some example doesn’t work anymore.
+
Examples (doctest)
+
doctest is a great way to provide examples in your code documentation. These example will then be used as tests. Apparently it comes from Python community.
There are plenty of alternative solution. I provide the one I believe would be used by most people. So if you use github simply create an account on travis.
+
Add a .travis.yml file in your repo containing the content of the file here and remove the builds you don’t need. It will build your project using a lot of different GHC versions and environemnts.
+
If you are afraid by such its complexity you might just want to use this one:
If you didn’t declared your package to stackage, please do it. It isn’t much work. Just edit a file to add your package. And you’ll could be able to add another badge:
data MyData a b
+ = C1 a b -- ^ doc for constructor C1
+ | C2 a b -- ^ doc for constructor C2
+
+data MyData a b
+ = C { a :: TypeA -- ^ field a description
+ , b :: TypeB -- ^ field b description
+ }
+
Module:
+
{-|
+Module : MyModule
+Description: Short description
+Copyright : (c)
+License : MIT
+
+Here is a longer description of this module.
+With some code symbol @MyType@.
+And also a block of code:
+
+@
+data MyData = C Int Int
+
+myFunction :: MyData -> Int
+@
+
+-}
+
Documentation Structure:
+
module MyModule (
+ -- * Classes
+ C(..),
+ -- * Types
+ -- ** A data type
+ T,
+ -- ** A record
+ R,
+ -- * Some functions
+ f, g
+ ) where
+
That will generate headings.
+
Other Random Ideas
+
In Haskell we have great tools like hayoo! and hoogle.
+
And hackage and stackage provide also a lot of informations.
+
But generally we lack a lot of Tutorials and Guides. This post was an attempt to help people making more of them.
+
But there are other good ideas to help improve the situation.
+
Create a doc with link to best practices
+
In clojure when you create a new project using lein new my-project a directory doc is created for you. It contains a file with a link to this blog post:
If you try to search for some clojure function on a search engine there is a big chance the first result will link to:
+
+
clojuredocs.org: try to search for reduce, update-in or index for example
+
+
For each symbol necessiting a documentation. You don’t only have the details and standard documentation. You’ll also get:
+
+
Responsive Design (sometime you want to look at documentation on a mobile)
+
Contributed Examples
+
Contributed See Also section
+
Contributed notes/comments
+
+
clojuredocs.org is an independant website from the official Clojure website.
+
Most of the time, if you google the function you search you end up on clojuredocs for wich there are many contributions.
+
Currently stackage is closer to these feature than hackage. Because on stackage you have access to the README and also some comments by package.
+
I believe it would be more efficient to have at least a page by module and why not a page by symbol (data, functions, typeclasses…).
+
For example, we could provide details about foldl for example. Also as there would be less information to display, it will make the design cleaner.
+
Today, if you want to help documenting, you need to make a PR to the source of some library. While if we had an equivalent to clojuredocs for Haskell, adding documentation would simply be a few clicks away:
+
+
login
+
add/edit some example, comments, see-also section
+
+
There are more than 23k people on /r/haskell. If only 1% of them would take 10 minutes adding a bit of documentation it will certainly change a lot of things in the percieved documentation quality.
+
And last but not least,
+
Design is important
+
+
+
+
Design is a vague word. A good design should care not only about how something look, but also how users will interact with it. For example by removing things to focus on the essential.
+
When I stumble upon some random blog post or random specification in the Haskell community, I had too much a feeling of old fashioned design.
+
If you look at node.js community lot of their web page look cleaner, easier to read and in the end, more user friendly.
+
Haskell is very different from node, I wouldn’t like to replace all long and precise documentation with short human unprecise concepts. I don’t want to transform scientific papers by tweets.
+
But like the scientific community has upgraded with the use of LaTeX, I believe we could find something similar that would make, very clean environment for most of us. A kind of look and feel that will be
+
+
modern
+
device friendly (either on computer, mobile, tablet)
+
efficient, focus on what is most important and is helpful
+
]]>
+
+
+ Vim as IDE
+
+ http://yannesposito.com/Scratch/fr/blog/Vim-as-IDE/index.html
+ 2014-12-07T00:00:00Z
+ 2014-12-07T00:00:00Z
+
+
+
+
+
tlpl: Comment utiliser vim comme une IDE très efficace
+
In Learn Vim Progressively I’ve show how Vim is great for editing text, and navigating in the same file (buffer). In this short article you’ll see how I use Vim as an IDE. Mainly by using some great plugins.
+
+
Vim Plugin Manager
+
There are a lot of Vim plugins. To manage them I use vim-plug.
☞ Note I have two parts in my .vimrc. The first part contains the list of all my plugins. The second part contains the personal preferences I setted for each plugin. I’ll separate each part by ... in the code.
+
+
Survival
+
Colorscheme
+
+
+
+
Before anything, you should protect your eyes using a readable and low contrast colorscheme.
+
For this I use solarized dark. To add it, you only have to write this in your ~/.vimrc file:
You should be able to see and destroy trailing whitespaces.
+
+
+
+
Plug 'bronson/vim-trailing-whitespace'
+
You can clean trailing whitespace with :FixWhitespace.
+
And also you should see your 80th column.
+
if (exists('+colorcolumn'))
+ set colorcolumn=80
+ highlight ColorColumn ctermbg=9
+endif
+
+
+
+
File Management
+
One of the most important hidden skills in programming is the ability to search and find files in your projects.
+
The majority of people use something like NERDTree. This is the classical left column with a tree of files of your project. I stopped to use this. And you should probably too.
+
I switched to unite. No left column lost. Faster to find files. Mainly it works like Spotlight on OS X.
+
First install ag (the silver search). If you don’t know ack or ag your life is going to be upgraded. This is a simple but essential tool. It is mostly a grep on steroids.
+
" Unite
+" depend on vimproc
+" ------------- VERY IMPORTANT ------------
+" you have to go to .vim/plugin/vimproc.vim and do a ./make
+" -----------------------------------------
+Plug 'Shougo/vimproc.vim'
+Plug 'Shougo/unite.vim'
+
+...
+
+let g:unite_source_history_yank_enable = 1
+try
+ let g:unite_source_rec_async_command='ag --nocolor --nogroup -g ""'
+ call unite#filters#matcher_default#use(['matcher_fuzzy'])
+catch
+endtry
+" search a file in the filetree
+nnoremap <space><space> :split<cr> :<C-u>Unite -start-insert file_rec/async<cr>
+" reset not it is <C-l> normally
+:nnoremap <space>r <Plug>(unite_restart)
+
Now type space twice. A list of files appears. Start to type some letters of the file you are searching for. Select it, type return and bingo the file opens in a new horizontal split.
+
+
+
+
If something goes wrong just type <space>r to reset the unite cache.
+
Now you are able to search file by name easily and efficiently.
+
Now search text in many files. For this you use ag:
+
Plug 'rking/ag.vim'
+...
+" --- type ° to search the word in all files in the current dir
+nmap ° :Ag <c-r>=expand("<cword>")<cr><cr>
+nnoremap <space>/ :Ag
+
Don’t forget to add a space after the :Ag.
+
These are two of the most powerful shortcut for working in a project. using ° which is nicely positioned on my azerty keyboard. You should use a key close to *.
+
So what ° is doing? It reads the string under the cursor and search for it in all files. Really useful to search where a function is used.
+
If you type <space>/ followed by a string, it will search for all occurrences of this string in the project files.
+
So with this you should already be able to navigate between files very easily.
+
Language Agnostic Plugins
+
Git
+
+
+
+
Show which line changed since your last commit.
+
Plug 'airblade/vim-gitgutter'
+
And the “defacto” git plugin:
+
Plug 'tpope/vim-fugitive'
+
You can reset your changes from the latest git commit with :Gread. You can stage your changes with :Gwrite.
Just select and type Return then space. Type Return many type to change the alignments.
+
If you want to align the second column, Return then 2 then space.
+
+
+
+
Basic auto completion: C-n & C-p
+
Vim has a basic auto completion system. The shortcuts are C-n and C-p while you are in insert mode. This is generally good enough in most cases. For example when I open a file not in my configured languages.
+
Haskell
+
My current Haskell programming environment is great!
+
Each time I save a file, I get a comment pointing to my errors or proposing me how to improve my code.
+
So here we go:
+
+
☞ Don’t forget to install ghc-mod with: cabal install ghc-mod
I use - for my leader because I use , a lot for its native usage.
+
+
-ht will highlight and show the type of the block under the cursor.
+
-hT will insert the type of the current block.
+
-hh will unhighlight the selection.
+
+
+
+
+
Clojure
+
+
+
+
My main language at work is Clojure. And my current vim environment is quite good. I lack the automatic integration to lein-kibit thought. If I have the courage I might do it myself one day. But due to the very long startup time of clojure, I doubt I’ll be able to make a useful vim plugin.
+
So mainly you’ll have real rainbow-parenthesis (the default values are broken for solarized).
+
I used the vim paredit plugin before. But it is too restrictive. Now I use sexp which feel more coherent with the spirit of vim.
Working with Clojure will becomre quite smoother. You can eval any part of your code, you must launch a Clojure REPL manually in another terminal thought.
+
Last words
+
I hope it will be useful.
+
Last but not least, if you want to use my vim configuration you can get it here:
Si vous êtes sous windows, téléchargez Haskell Platform et suivez les instructions pour utiliser Haskell LTS.
+
Si vous voulez savoir le pourquoi et le comment ; lisez le reste de l’article.
+
+
Pourquoi ?
+
La plus grande faiblesse d’Haskell n’a rien à voir avec le langage en lui-même mais avec son écosystème.
+
The main problem I’ll try to address is the one known as cabal hell. The community is really active in fixing the issue. I am very confident that in less than a year this problem will be one of the past. But to work today, I provide an install method that should reduce greatly two effects of cabal hell:
+
+
dependency error
+
lost time in compilation (poor polar bears)
+
+
With my actual installation method, you should minimize your headache and almost never hit a dependency error. But there could exists some. If you encounter any dependency error, ask gently to the package manager to port its package to stackage.
+
So to install copy/paste the following three lines in your terminal:
You can read the script and you will see that this is quite straightforward.
+
+
It downloads the latest GHC binary for you system and install it.
+
It does the same with the cabal program.
+
It updates your cabal config file to use Haskell LTS.
+
It enable profiling to libraries and executables.
+
It installs some useful binaries that might cause compilation error if not present.
+
+
As the version of libraries is fixed up until you update the Haskell LTS version, you should never use cabal sandbox. That way, you will only compile each needed library once. The compiled objects/binaries will be in your ~/.cabal directory.
+
Some Last Words
+
This script use the latest Haskell LTS. So if you use this script at different dates, the Haskell LTS might have changed.
+
While it comes to cabal hell, some solutions are sandboxes and nix. Unfortunately, sandboxes didn’t worked good enough for me after some time. Furthermore, sandboxes forces you to re-compile everything by project. If you have three yesod projects for example it means a lot of time and CPU. Also, nix didn’t worked as expected on OS X. So fixing the list of package to a stable list of them seems to me the best pragmatic way to handle the problem today.
+
From my point of view, Haskell LTS is the best step in the right direction. The actual cabal hell problem is more a human problem than a tool problem. This is a bias in most programmer to prefer resolve social issues using tools. There is nothing wrong with hackage and cabal. But for a package manager to work in a static typing language as Haskell, packages must work all together. This is a great strength of static typed languages that they ensure that a big part of the API between packages are compatible. But this make the job of package managing far more difficult than in dynamic languages.
+
People tend not to respect the rules in package numbering1. They break their API all the time. So we need a way to organize all of that. And this is precisely what Haskell LTS provide. A set of stable packages working all together. So if a developer break its API, it won’t work anymore in stackage. And whether the developer fix its package or all other packages upgrade their usage. During this time, Haskell LTS end-users will be able to develop without dependency issues.
tlpl: Apprenez comment commencer un nouveau projet Haskell. Avec en exemple le créateur de projet Haskell lui-même.
+
+
“Good Sir Knight, will you come with me to Camelot, and join us at the Round Table?”
+
+
In order to work properly with Haskell you need to initialize your environment. Typically, you need to use a cabal file, create some test for your code. Both, unit test and propositional testing (random and exhaustive up to a certain depth). You need to use git and generally hosting it on github. Also, it is recommended to use cabal sandboxes. And as bonus, an auto-update tool that recompile and retest on each file save.
+
In this article, we will create such an environment using a zsh script. Then we will write a Haskell project which does the same work as the zsh script. You will then see how to work in such an environment.
+
If you are starting to understand Haskell but consider yourself a beginner, this tutorial will show you how to make a real application using quite surprisingly a lot of features:
+
+
use colorized output
+
interact with a user in command line
+
read/write files
+
kind of parse a file (in fact, simply split it)
+
use a templating system (mustache: fill a data structure, write files)
+
make a HTTP GET request then parse the JSON answer and use it
+
use random
+
create a cabal package
+
add and use non source files to a cabal package
+
Test your code (both unit testing and property testing)
+
+
☞ zsh is by its nature more suitable to file manipulation. But the Haskell code is clearly more organized while quite terse for a multi-purpose language.
+
☞ holy-project is on hackage. It can be installed with cabal update && cabal install holy-project.
While the article is very good, I lacked some minor informations1. Inspired by it, I created a simple script to initialize a new Haskell project. During the process I improved some things a bit.
What does this script do that cabal init doesn’t do?
+
+
Use cabal sandbox
+
It initialize git with the right .gitignore file.
+
Use tasty to organize your tests (HUnit, QuickCheck and SmallCheck).
+
Use -Wall for ghc compilation.
+
Will make references to Holy Grail
+
Search your default github username via github api.
+
+
zsh really?
+
+
+
+
Developing the script in zsh was easy. But considering its size, it is worth to rewrite it in Haskell. Furthermore, it will be a good exercise.
+
Patricide
+
In a first time, we initialize a new Haskell project with holy-haskell.sh:
+
+> ./holy-haskell.sh
+Bridgekeeper: Stop!
+Bridgekeeper: Who would cross the Bridge of Death
+Bridgekeeper: must answer me these questions three,
+Bridgekeeper: ere the other side he see.
+You: Ask me the questions, bridgekeeper, I am not afraid.
+
+Bridgekeeper: What is the name of your project?
+> Holy project
+Bridgekeeper: What is your name? (Yann Esposito (Yogsototh))
+>
+Bridgekeeper: What is your email? (Yann.Esposito@gmail.com)
+>
+Bridgekeeper: What is your github user name? (yogsototh)
+>
+Bridgekeeper: What is your project in less than ten words?
+> Start your Haskell project with cabal, git and tests.
+Initialize git
+Initialized empty Git repository in .../holy-project/.git/
+Create files
+ .gitignore
+ holy-project.cabal
+ Setup.hs
+ LICENSE (MIT)
+ test/Test.hs
+ test/HolyProject/Swallow/Test.hs
+ src/HolyProject/Swallow.hs
+ test/HolyProject/Coconut/Test.hs
+ src/HolyProject/Coconut.hs
+ src/HolyProject.hs
+ src/Main.hs
+Cabal sandboxing, install and test
+...
+ many compilations lines
+...
+Running 1 test suites...
+Test suite Tests: RUNNING...
+Test suite Tests: PASS
+Test suite logged to: dist/test/holy-project-0.1.0.0-Tests.log
+1 of 1 test suites (1 of 1 test cases) passed.
+All Tests
+ Swallow
+ swallow test: OK
+ coconut
+ coconut: OK
+ coconut property: OK
+ 148 tests completed
+
+All 3 tests passed
+
+
+
+Bridgekeeper: What... is the air-speed velocity of an unladen swallow?
+You: What do you mean? An African or European swallow?
+Bridgekeeper: Huh? I... I don't know that.
+[the bridgekeeper is thrown over]
+Bridgekeeper: Auuuuuuuuuuuugh
+Sir Bedevere: How do you know so much about swallows?
+You: Well, you have to know these things when you're a king, you know.
+
+
The different steps are:
+
+
small introduction quotes
+
ask five questions – three question sir…
+
create the directory for the project
+
init git
+
create files
+
sandbox cabal
+
cabal install and test
+
run the test directly in the terminal
+
small goodbye quotes
+
+
Features to note:
+
+
color in the terminal
+
check some rules on the project name
+
random message if error
+
use ~/.gitconfig file in order to provide a default name and email.
+
use the github API which returns JSON to get the default github user name.
+
+
So, apparently nothing too difficult to achieve.
+
We should now have an initialized Haskell environment for us to work. The first thing you should do, is to go into this new directory and launch ‘./auto-update’ in some terminal. I personally use tmux on Linux or the splits in iTerm 2 on Mac OS X. Now, any modification of a source file will relaunch a compilation and a test.
The haskell version is made by hand where zsh already had a capitalize operation on string with many words. Here is the difference between the shell and haskell way (note I splitted the effect of concatMap as map and concat):
In Haskell, while possible, we shouldn’t put the file content in the source code. We have a relatively easy way to include external file in a cabal package. This is what we will be using.
+
Furthermore, we need a templating system to replace small part of the static file by computed values. For this task, I choose to use hastache, a Haskell implementation of Mustache templates3.
+
Add external files in a cabal project
+
Cabal provides a way to add files which are not source files to a package. You simply have to add a Data-Files: entry in the header of the cabal file:
Now we simply have to create our files at the specified path. Here is for example the first lines of the LICENSE file.
+
The MIT License (MIT)
+
+Copyright (c) {{year}}{{author}}
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+...
+
It will be up to our program to replace the {{year}} and {{author}} at runtime. We have to find the files. Cabal will create a module named Paths_holy_project. If we import this module we have the function genDataFileName at our disposal. Now we can read the files at runtime like this:
A first remark is for portability purpose we shouldn’t use String for file path. For example on Windows / isn’t considered as a subdirectory character. To resolve this problem we will use FilePath:
In order to use hastache we can either create a context manually or use generics to create a context from a record. This is the last option we will show here. So in a first time, we need to import some modules and declare a record containing all necessary informations to create our project.
We use external files in mustache format. We ask question to our user to fill a data structure. We use this data structure to create a context. Hastache use this context with the external files to create the project files.
+
Git and Cabal
+
+
+
+
We need to initialize git and cabal. For this we simply call external command with the system function.
We want to retrieve the ~/.gitconfig file content and see if it contains a name and email information. We will need to access to the HOME environment variable. Also, as we use bytestring package for hastache, let’s take advantage of this library.
We could notice, getNameAndMail doesn’t read the full file and stop at the first occurrence of name and mail.
+
Use the github API
+
+
+
+
The task seems relatively easy, but we’ll see there will be some complexity hidden. Make a request on https://api.github.com/search/users?q=<email>. Parse the JSON and get the login field of the first item.
+
So the first problem to handle is to connect an URL. For this we will use the http-conduit package.
But, after some research, I discovered we must declare an User-Agent in the HTTP header to be accepted by the github API. So we have to change the HTTP Header, and our code became slightly more complex:
So now, we have a String containing a JSON representation. In javascript we would have used login=JSON.parse(body).items[0].login. How does Haskell will handle it (knowing the J in JSON is for Javascript)?
+
First we will need to add the lens-aeson package and use it that way:
It looks ugly, but it’s terse. In fact each function (^?), key and nth has some great mathematical properties and everything is type safe. Unfortunately I had to make my own jsonValueToString. I hope I simply missed a simpler existing function.
The HolyProject.hs file contains mostly the code that ask questions, show errors and copy files using hastache.
+
One of the benefits in modularizing the code is that our main code is clearer. Some functions are declared only in a module and are not exported. This help us hide technical details. For example, the modification of the HTTP header to use the github API.
+
Documenting
+
+
+
+
We didn’t take much advantage of the project structure yet. A first thing is to generate some documentation. Before most function I added comment starting with -- |. These comment will be used by haddock to create a documentation. First, you need to install haddock manually.
And magically, you’ll have a documentation in dist/doc/html/holy-project/index.html.
+
Tests
+
While the Haskell static typing is quite efficient to prevent entire classes of bugs, Haskell doesn’t discard the need to test to minimize the number of bugs.
+
Unit Testing with HUnit
+
+
+
+
It is generally said to test we should use unit testing for code in IO and QuickCheck or SmallCheck for pure code.
+
A unit test example on pure code is in the file test/HolyProject/Swallow/Test.hs:
Note swallow is (++). We group tests by group. Each group can contain some test suite. Here we have a test suite with only one test. The (@=?) verify the equality between its two parameters.
+
So now, we could safely delete the directory test/HolyProject/Swallow and the file src/HolyProject/Swallow.hs. And we are ready to make our own real world unit test. We will first test the module HolyProject.GithubAPI. Let’s create a file test/HolyProject/GithubAPI/Test.hs with the following content:
You have to modify your cabal file. More precisely, you have to add HolyProject.GithubAPI in the exposed modules of the library secion). You also have to update the test/Test.hs file to use GithubAPI instead of Swallow.
+
So we have our example of unit testing using IO. We search the github nickname for some people I know and we verify github continue to give the same answer as expected.
+
Property Testing with SmallCheck and QuickCheck
+
+
+
+
When it comes to pure code, a very good method is to use QuickCheck and SmallCheck. SmallCheck will verify all cases up to some depth about some property. While QuickCheck will verify some random cases.
+
As this kind of verification of property is mostly doable on pure code, we will test the StringUtils module.
+
So don’t forget to declare HolyProject.StringUtils in the exposed modules in the library section of your cabal file. Remove all references to the Coconut module.
+
Modify the test/Test.hs to remove all references about Coconut. Create a test/HolyProject/StringUtils/Test.hs file containing:
+All Tests
+ StringUtils
+ SC projectNameFromString idempotent: OK
+ 206 tests completed
+ SC capitalize idempotent: OK
+ 1237 tests completed
+ QC projectNameFromString idempotent: FAIL
+ *** Failed! Falsifiable (after 19 tests and 5 shrinks):
+ "a a"
+ Use --quickcheck-replay '18 913813783 2147483380' to reproduce.
+ GithubAPI
+ Yann: OK
+ Jasper: OK
+
+1 out of 5 tests failed
+
+
The test fail, but this is not an error. Our capitalize function shouldn’t be idempotent. I simply added this test to show what occurs when a test fail. If you want to look more closely to the error you could do this:
It is important to use ./interact instead of ghci. Because we need to tell ghci how to found the package installed.
+
Apparently, SmallCheck didn’t found any counter example. I don’t know how it generates Strings and using deeper search is really long.
+
Conclusion
+
+
+
+
Congratulation!
+
Now you could start programming in Haskell and publish your own cabal package.
+
+
+
+
For example, you have to install the test libraries manually to use cabal test.↩
+
There is no easy way to do something like name=$(ask name). Simply because $(ask name) run in another process which doesn’t get access to the standard input↩
+
Having a good level of power in templates is very difficult. imho Mustache has made the best compromise.↩
tlpl: Comment déterminer de la façon la plus rationnelle possible le meilleur framework work relativement à vos besoins. Cliquez ici pour aller au résultats. Cet article n’est disponible qu’en anglais.
+
+
This is it.
+You’ve got the next big idea.
+You just need to make a very simple web application.
+
It sounds easy! You just need to choose a good modern web framework, when suddenly:
+
+
After your brain stack overflowed, you decide to use a very simple methodology. Answer two questions:
+
Which language am I familiar with?
+What is the most popular web framework for this language?
+
Great! This is it.
+
But, you continually hear this little voice.
+
+
“You didn’t made a bad choice, yes. But …
+you hadn’t made the best either.”
+
+
This article try to determine in the most objective and rational way the best(s) web framework(s) depending on your needs. To reach this goal, I will provide a decision tool in the result section.
+
I will use the following methodology:
+
Methodology
+
+
Model how to make choice
+
+
choose important parameters
+
organize (hierarchize) them
+
write down an objective chooser
+
+
Grab objective quantified informations about web frameworks relatively to choosen parameters
+
Sanitize your data in order to handle imprecisions, lack of informations…
+
Apply the model of choice to your informations
+
+
+
☞ Important Note
+I am far from happy to the actual result. There are a lot of biases, for example in the choice of the parameters. The same can be said about the data I gathered. I am using very imprecise informations. But, as far as I know, this is the only article which use many different parameters to help you choose a web framework.
Here are the important features (properties/parameters) I selected to make the choice:
+
+
Popularity, which correlate with:
+
+
number of tested libraries
+
facility to find learning material
+
ability to find another developer to work with
+
+
Efficiency, which is generally correlated to:
+
+
how much processing power you’ll need per user
+
maintenance price per user
+
how long the user will wait to see/update data
+
+
Expressiveness, which is generally correlated to:
+
+
faster development
+
flexibility, adaptability
+
+
Robustness, which correlate with:
+
+
security
+
fewer bugs
+
+
+
Each feature is quite important and mostly independant from each other. I tried to embrace most important topics concerning web frameworks with these four properties. I am fully concious some people might lack another important feature. Nonetheless the methodology used here can be easily replicated. If you lack an important property add it at will and use this choice method.
+
Also each feature is very hard to measure with precision. This is why we will only focus on order of magnitude.
+
For each property a framework could have one of the six possible values: Excellent, Very Good, Good, Medium, Bad or Very Bad
+
So how to make a decision model from these informations?
+
One of the most versatile method is to give a weight for each cluster value. And to select the framework maximizing this score:
We don’t make any difference between excellent and very good in popularity.
+
Concerning efficient framework in excellent cluster will have 2 more points than the “very good” cluster.
+
+
So for each framework we compute its score relatively to a weighted table. And we select the best(s).
+
Example: Using this hypothetic framework and the preceeding table.
+
+
+
+
+
Expressiveness
+
Popularity
+
Efficiency
+
Robustness
+
+
+
+
+
yog
+
Excellent
+
Very Bad
+
Medium
+
Very Good
+
+
+
+
score(yog) = 10 + 0 + 4 + 8 = 22
+
+
Most needs should be expressed by such a weighted table. In the result section, we will discuss this further.
+
It is now time to try to get these measures.
+
Objective measures
+
None of the four properties I choosen can be measured with perfect precision. But we could get the order of magnitude for each.
+
I tried to focus on the framework only. But it is often easier to start by studying the language first.
+
For example, I have datas about popularity by language and I also have different datas concerning popularity by framework. Even if I use only the framework focused datas in my final decision model, it seemed important to me to discuss about the datas for the languages. The goal is to provide a tool to help decision not to give a decision for you.
+
Popularity
+
RedMonk Programming Language Rankings (January 2013) provide an apparent good measure of popularity. While not perfect the current measure feel mostly right. They create an image using stack overflow and github data. Vertical correspond to the number of questions on stackoverflow. Horizontal correspond to the number of projects on github.
+
If you look at the image, your eye can see about four clusters. The 1st cluster correspond to mainstream languages:
+
+
Most developer know at least one of these language.
+
The second cluster is quite bigger. It seems to correspond to languages with a solid community behind them.
+
+
I don’t get into detail, but you could also see third and fourth tier popular languages.
I don’t thing I could find easily web frameworks for third or fourth tier languages.
+
For now, I only talked about language popularity. But what about framework popularity? I made a test using number of question on stackoverflow only. Then by dividing by two for each 6 cluster:
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
%
+
+
+
+
+
Excellent
+
Ruby
+
Rails
+
176208
+
100%
+
+
+
Very Good
+
Python
+
Django
+
57385
+
<50%
+
+
+
+
Java
+
Servlet
+
54139
+
+
+
+
+
Java
+
Spring
+
31641
+
+
+
+
+
Node.js
+
node.js
+
27243
+
+
+
+
+
PHP
+
Codeigniter
+
21503
+
+
+
+
+
Groovy
+
Grails
+
20222
+
+
+
+
Good
+
Ruby
+
Sinatra
+
8631
+
<25%
+
+
+
+
Python
+
Flask
+
7062
+
+
+
+
+
PHP
+
Laravel
+
6982
+
+
+
+
+
PHP
+
Kohana
+
5959
+
+
+
+
+
Node.js
+
Express
+
5009
+
+
+
+
Medium
+
PHP
+
Cake
+
4554
+
<13%
+
+
+
+
C♯
+
ServiceStack
+
3838
+
+
+
+
+
Scala
+
Play
+
3823
+
+
+
+
+
Java
+
Wicket
+
3819
+
+
+
+
+
Dart
+
Dart
+
3753
+
+
+
+
+
PHP
+
Slim
+
3361
+
+
+
+
+
Python
+
Tornado
+
3321
+
+
+
+
+
Scala
+
Lift
+
2844
+
+
+
+
+
Go
+
Go
+
2689
+
+
+
+
Bad
+
Java
+
Tapestry
+
1197
+
<6%
+
+
+
+
C♯
+
aspnet
+
1000
+
+
+
+
+
Haskell
+
Yesod
+
889
+
+
+
+
+
PHP
+
Silex
+
750
+
+
+
+
+
PHP
+
Lithium
+
732
+
+
+
+
+
C♯
+
nancy
+
705
+
+
+
+
Very bad
+
Java
+
Grizzly
+
622
+
<3%
+
+
+
+
Erlang
+
Cowboy
+
568
+
+
+
+
+
Perl
+
Dancer
+
496
+
+
+
+
+
PHP
+
Symphony2
+
491
+
+
+
+
+
Go
+
Revel
+
459
+
+
+
+
+
Clojure
+
Compojure
+
391
+
+
+
+
+
Perl
+
Mojolicious
+
376
+
+
+
+
+
Scala
+
Scalatra
+
349
+
+
+
+
+
Scala
+
Finagle
+
336
+
+
+
+
+
PHP
+
Phalcon
+
299
+
+
+
+
+
js
+
Ringo
+
299
+
+
+
+
+
Java
+
Gemini
+
276
+
+
+
+
+
Haskell
+
Snap
+
263
+
+
+
+
+
Perl
+
Plack
+
257
+
+
+
+
+
Erlang
+
Elli
+
230
+
+
+
+
+
Java
+
Dropwizard
+
188
+
+
+
+
+
PHP
+
Yaf
+
146
+
+
+
+
+
Java
+
Play1
+
133
+
+
+
+
+
Node.js
+
Hapi
+
131
+
+
+
+
+
Java
+
Vertx
+
60
+
+
+
+
+
Scala
+
Unfiltered
+
42
+
+
+
+
+
C
+
onion
+
18
+
+
+
+
+
Clojure
+
http-kit
+
17
+
+
+
+
+
Perl
+
Kelp
+
16
+
+
+
+
+
PHP
+
Micromvc
+
13
+
+
+
+
+
Lua
+
Openresty
+
8
+
+
+
+
+
C++
+
cpoll-cppsp
+
5
+
+
+
+
+
Clojure
+
Luminus
+
3
+
+
+
+
+
PHP
+
Phreeze
+
1
+
+
+
+
+
As we can see, our framework popularity indicator can be quite different from its language popularity. For now I didn’t found a nice way to merge the results from RedMonk with these one. So I’ll use these unperfect one. Hopefully the order of magninute is mostly correct for most framework.
+
Efficiency
+
Another objective measure is efficiency. We all know benchmarks are all flawed. But they are the only indicators concerning efficiency we have.
+
I used the benchmark from benchmarksgame. Mainly, there are five clusters:
+
+
+
+
1x→2x
+
C, C++
+
+
+
2x→3x
+
Java 7, Scala, OCamL, Haskell, Go, Common LISP
+
+
+
3x→10x
+
C♯, Clojure, Racket, Dart
+
+
+
10x→30x
+
Erlang
+
+
+
30x→
+
PHP, Python, Perl, Ruby, JRuby
+
+
+
+
Remarks concerning some very slow languages:
+
+
PHP ; huge variations, can be about 1.5x C speed in best case.
+
Python ; huge variations, can be about 1.5x C speed in best case
+
Perl ; Can be about 3x C speed in best case
+
Ruby, JRuby ; mostly very slow.
+
+
This is a first approach. The speed of the language for basic benchmarks. But, here we are interrested in web programming. Fortunately techempower has made some tests focused on most web frameworks:
These benchmark doesn’t fit well with our needs. The values are certainly quite imprecise to your real usage. The goal is just to get an order of magnitude for each framework. Another problem is the high number of informations.
+
As always, we should remember these informations are also imprecise. So I simply made some classes of efficiency.
+
Remark: I separated the clusters by using power of 2 relatively to the fastest.
+
+
+
+
Cluster
+
Language
+
Framework
+
#nb
+
slowness
+
+
+
+
+
Excellent
+
C++
+
cpoll-cppsp
+
114,711
+
1×
+
+
+
+
Jav
+
gemini
+
105,204
+
+
+
+
+
Lua
+
openresty
+
93,882
+
+
+
+
+
Jav
+
servlet
+
90,580
+
+
+
+
+
C++
+
cpoll-pool
+
89,167
+
+
+
+
+
Go
+
go
+
76,024
+
+
+
+
+
Sca
+
finagle
+
68,413
+
+
+
+
+
Go
+
revel
+
66,990
+
+
+
+
+
Jav
+
rest-express
+
63,209
+
+
+
+
Very Good
+
Jav
+
wicket
+
48,772
+
>2×
+
+
+
+
Sca
+
scalatra
+
48,594
+
+
+
+
+
Clj
+
http-kit
+
42,703
+
+
+
+
+
Jav
+
spring
+
36,643
+
>3×
+
+
+
+
PHP
+
php
+
36,605
+
+
+
+
+
Jav
+
tapestry
+
35,032
+
+
+
+
+
Clj
+
compojure
+
32,088
+
+
+
+
+
JS
+
ringo
+
31,962
+
+
+
+
+
Jav
+
dropwizard
+
31,514
+
+
+
+
+
Clj
+
luminus
+
30,672
+
+
+
+
Good
+
Sca
+
play-slick
+
29,950
+
>4×
+
+
+
+
Sca
+
unfiltered
+
29,782
+
+
+
+
+
Erl
+
elli
+
28,862
+
+
+
+
+
Jav
+
vertx
+
28,075
+
+
+
+
+
JS
+
nodejs
+
27,598
+
+
+
+
+
Erl
+
cowboy
+
24,669
+
+
+
+
+
C
+
onion
+
23,649
+
+
+
+
+
Hkl
+
yesod
+
23,304
+
+
+
+
+
JS
+
express
+
22,856
+
>5×
+
+
+
+
Sca
+
play-scala
+
22,372
+
+
+
+
+
Jav g
+
rizzly-jersey
+
20,550
+
+
+
+
+
Py
+
tornado
+
20,372
+
>6×
+
+
+
+
PHP
+
phalcon
+
18,481
+
+
+
+
+
Grv
+
grails
+
18,467
+
+
+
+
+
Prl
+
plack
+
16,647
+
>7×
+
+
+
+
PHP
+
yaf
+
14,388
+
+
+
+
Medium
+
JS
+
hapi
+
11,235
+
>10×
+
+
+
+
Jav
+
play1
+
9,979
+
+
+
+
+
Hkl
+
snap
+
9,196
+
+
+
+
+
Prl
+
kelp
+
8,250
+
+
+
+
+
Py
+
flask
+
8,167
+
+
+
+
+
Jav
+
play-java
+
7,905
+
+
+
+
+
Jav p
+
lay-java-jpa
+
7,846
+
+
+
+
+
PHP
+
micromvc
+
7,387
+
+
+
+
+
Prl
+
dancer
+
5,040
+
>20×
+
+
+
+
Prl
+
mojolicious
+
4,371
+
+
+
+
+
JS
+
ringo-conv
+
4,249
+
+
+
+
+
Py
+
django
+
4,026
+
+
+
+
+
PHP
+
codeigniter
+
3,809
+
>30×
+
+
+
Bad
+
Rby
+
rails
+
3,445
+
+
+
+
+
Sca
+
lift
+
3,311
+
+
+
+
+
PHP
+
slim
+
3,112
+
+
+
+
+
PHP
+
kohana
+
2,378
+
>40×
+
+
+
+
PHP
+
silex
+
2,364
+
+
+
+
Very Bad
+
PHP
+
laravel
+
1,639
+
>60×
+
+
+
+
PHP
+
phreeze
+
1,410
+
+
+
+
+
PHP
+
lithium
+
1,410
+
+
+
+
+
PHP
+
fuel
+
1,410
+
+
+
+
+
PHP
+
cake
+
1,287
+
>80×
+
+
+
+
PHP
+
symfony2
+
879
+
>100×
+
+
+
+
C#
+
aspnet-mvc
+
871
+
+
+
+
+
Rby
+
sinatra
+
561
+
>200×
+
+
+
+
C#
+
servicestack
+
51
+
+
+
+
+
Dar
+
dart
+
0
+
+
+
+
+
C#
+
nancy
+
0
+
+
+
+
+
Prl
+
web-simple
+
0
+
+
+
+
+
These are manually made clusters. But you get the idea. Certainly, some framework could jump between two different clusters. So this is something to remember. But as always, the order of magnitude is certainly mostly right.
+
Expressiveness
+
Now, how to objectively measure expressiveness?
+
RedMonk had a very good idea to find an objective (while imprecise) measure of each language expressiveness. Read this article for details.
+
After filtering languages suitable for web development, we end up with some clusters:
+
+
+
+
Cluster
+
Languages
+
+
+
+
+
Excellent
+
Coffeescript, Clojure, Haskell
+
+
+
Very Good
+
Racket, Groovy, R, Scala, OCamL, F♯, Erlang, Lisp, Go
+
+
+
Medium
+
Perl, Python, Objective-C, Scheme, Tcl, Ruby
+
+
+
Bad
+
Lua, Fortran (free-format), PHP, Java, C++, C♯
+
+
+
Very Bad
+
Assembly, C, Javascript,
+
+
+
+
Unfortunately there is no information about dart. So I simply give a very fast look at the syntax. As it looked a lot like javascript and js is quite low. I decided to put it close to java.
+
Also an important remark, javascript score very badly here while coffeescript (compiling to js) score “excellent”. So if you intend to use a javascript framework but only with coffescript that should change substantially the score. As I don’t believe it is the standard. Javascript oriented framework score very badly regarding expressiveness.
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Grv
+
grails
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Medium
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Bad
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
Very Bad
+
C
+
onion
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
+
Robustness
+
I couldn’t find any complete study to give the number of bug relatively to each framework/language.
+
But one thing I saw from experience is the more powerful the type system the safest your application is. While the type system doesn’t remove completely the need to test your application a very good type system tend to remove complete classes of bug.
+
Typically, not using pointer help to reduce the number of bugs due to bad references. Also, using a garbage collector, reduce greatly the probability to access unallocated space.
+
+
From my point of view, robustness is mostly identical to safety.
+
Here are the clusters:
+
+
+
+
Excellent
+
Haskell, Scheme, Erlang
+
+
+
Very Good
+
Scala, Java, Clojure
+
+
+
Good
+
Ruby, Python, Groovy, javascript, PHP
+
+
+
Medium
+
C++, C#, Perl, Objective-C, Go, C
+
+
+
+
So applying this to frameworks gives the following clusters:
+
+Click here to show/hide the table for frameworks
+
+
+
+
+
+
Cluster
+
Language
+
Framework
+
+
+
+
+
Excellent
+
Erl
+
elli
+
+
+
+
Erl
+
cowboy
+
+
+
+
Hkl
+
snap
+
+
+
+
Hkl
+
yesod
+
+
+
Very Good
+
Clj
+
luminus
+
+
+
+
Clj
+
http-kit
+
+
+
+
Clj
+
compojure
+
+
+
+
Jav
+
play1
+
+
+
+
Jav
+
vertx
+
+
+
+
Jav
+
gemini
+
+
+
+
Jav
+
spring
+
+
+
+
Jav
+
wicket
+
+
+
+
Jav
+
servlet
+
+
+
+
Jav
+
tapestry
+
+
+
+
Jav
+
play-java
+
+
+
+
Jav
+
dropwizard
+
+
+
+
Jav
+
rest-express
+
+
+
+
Jav
+
play-java-jpa
+
+
+
+
Jav
+
grizzly-jersey
+
+
+
+
Sca
+
lift
+
+
+
+
Sca
+
finagle
+
+
+
+
Sca
+
scalatra
+
+
+
+
Sca
+
play-scala
+
+
+
+
Sca
+
play-slick
+
+
+
+
Sca
+
unfiltered
+
+
+
Good
+
Grv
+
grails
+
+
+
+
JS
+
hapi
+
+
+
+
JS
+
ringo
+
+
+
+
JS
+
nodejs
+
+
+
+
JS
+
express
+
+
+
+
JS
+
ringo-conv
+
+
+
+
Lua
+
openresty
+
+
+
+
PHP
+
php
+
+
+
+
PHP
+
yaf
+
+
+
+
PHP
+
cake
+
+
+
+
PHP
+
fuel
+
+
+
+
PHP
+
slim
+
+
+
+
PHP
+
silex
+
+
+
+
PHP
+
kohana
+
+
+
+
PHP
+
laravel
+
+
+
+
PHP
+
lithium
+
+
+
+
PHP
+
phalcon
+
+
+
+
PHP
+
phreeze
+
+
+
+
PHP
+
micromvc
+
+
+
+
PHP
+
symfony2
+
+
+
+
PHP
+
codeigniter
+
+
+
+
Py
+
flask
+
+
+
+
Py
+
django
+
+
+
+
Py
+
tornado
+
+
+
+
Rby
+
rails
+
+
+
+
Rby
+
sinatra
+
+
+
Medium
+
C
+
onion
+
+
+
+
C#
+
nancy
+
+
+
+
C#
+
aspnet-mvc
+
+
+
+
C#
+
servicestack
+
+
+
+
C++
+
cpoll-pool
+
+
+
+
C++
+
cpoll-cppsp
+
+
+
+
Dar
+
dart
+
+
+
+
Go
+
go
+
+
+
+
Go
+
revel
+
+
+
+
Prl
+
kelp
+
+
+
+
Prl
+
plack
+
+
+
+
Prl
+
dancer
+
+
+
+
Prl
+
web-simple
+
+
+
+
Prl
+
mojolicious
+
+
+
+
+
The result
+
For the result I initialized the table with my own needs.
+
And I am quite happy it confirms my current choice. I sware I didn’t given yesod any bonus point. I tried to be the most objective and factual as possible.
+
Now, it is up to you to enter your preferences.
+
On each line you could change how important a feature is for you. From essential to unsignificant. Of course you could change the matrix at will.
+
I just show a top 10 frameworks. In order to give a more understandable measure I provide the log of the score.
+
+
+
+
+
+Excellent
+
+
+Very good
+
+
+Good
+
+
+Medium
+
+
+Bad
+
+
+Very bad
+
+
+Importance
+
+
+
+
+Expressiveness
+
+
+
+
+Popularity
+
+
+
+
+Efficiency
+
+
+
+
+Robustness
+
+
+
+
+Click to force refresh
+
+
+
+
+
+
+
I didn’t had the courage in explaining in what the scoring system is good. Mostly, if you use product instead of sum for the score you could use power of e for the values in the matrix. And you could see the matrix as a probability matrix (each line sum to 1). Which provide a slighly better intuition on whats going on.
+
Remember only that values are exponential. Do not double an already big value for example the effect would be extreme.
+
Conclusion
+
All of this is based as most as I could on objective data. The choice method seems both rather rational and classical. It is now up to you to edit the score matrix to set your needs.
+
I know that in the current state there are many flaws. But it is a first system to help make a choice rationally.
+
I encourage you to go further if you are not satisfied by my method.
+
The source code for the matrix shouldn’t be too hard to read. Just read the source of this webpage. You could change the positionning of some frameworks if you believe I made some mistake by placing them in some bad clusters.
+
So I hope this tool will help you in making your life easier.
Hakyll peut être vu comme un cms minimaliste. D’une façon plus générale, il s’agit d’une bibliothèque qui facilite la création automatique de fichiers.
+
D’un point de vue utilisateur voici comment j’écris mes articles :
+
+
J’ouvre un éditeur de texte (vim dans mon cas). J’édite un fichier markdow qui ressemble à ça :
J’ouvre mon navigateur et je rafraichis de temps en temps pour voir les changements.
+
Une fois satisfait, je lance un script minimal qui fait grosso modo un simple git push. Mon blog est hébergé sur github.
+
+
A ne pas y regarder de trop près, on peut réduire le rôle d’Hakyll à :
+
+
Créer (resp. mettre à jour) un fichier html lorsque je crée (resp. modifie) un fichier markdown.
+
+
Bien que cela semble facile, il y a de nombreux détails cachés :
+
+
Ajouter des métadatas comme des mots clés
+
Créer un page archive qui contient la liste de tous les articles
+
Gérer les fichier statiques
+
Créer un flux rss
+
Filtrer le contenu
+
Gérer les dépendances
+
+
Le travail d’Hakyll est de vous aider avec tout ça. Commençons par expliquer les concepts basiques.
+
Les concepts et la syntaxe
+
+
+
+
Pour chaque fichier que vous créer, il faut fournir :
+
+
un chemin de destination
+
une liste de filtres du contenu
+
+
Commençons par le cas le plus simple ; les fichiers statiques (images, fontes, etc…) Généralement, vous avec un répertoire source (ici le répertoire courant) et une répertoire destination _site.
Ce programme va copier static/foo.jpg dans _site/static/foo.jpg. C’est un peu lourd pour un simple cp. Maintenant comment faire pour transformer automatiquement un fichier markdown dans le bon html?
Mais horreur ! _site/posts/cthulhu.html n’est pas un html complet. Il ne possède ni header, ni footer, etc… C’est ici que nous utilisons des templates. J’ajoute une nouvelle directive dans le bloc “compile”.
La suite de l’article est en Anglais. Je la traduirai volontier si suffisamment de personnes me le demande gentillement.
+
Real customization
+
Now that we understand the basic functionality. How to:
+
+
use SASS?
+
add keywords?
+
simplify url?
+
create an archive page?
+
create an rss feed?
+
filter the content?
+
add abbreviations support?
+
manage two languages?
+
+
Use SASS
+
That’s easy. Simply call the executable using unixFilter. Of course you’ll have to install SASS (gem install sass). And we also use compressCss to gain some space.
Creating an archive start to be difficult. There is an example in the default Hakyll example. Unfortunately, it assumes all posts prefix their name with a date like in 2013-03-20-My-New-Post.md.
+
I migrated from an older blog and didn’t want to change my url. Also I prefer not to use any filename convention. Therefore, I add the date information in the metadata published. And the solution is here:
And base.html is a standard template (simpler than post.html).
+
archiveCtx provide a context containing an html representation of a list of posts in the metadata named posts. It will be used in the templates/archive.html file with $posts$.
postList returns an html representation of a list of posts given an Item sort function. The representation will apply a minimal template on all posts. Then it concatenate all the results. The template is post-item.html:
generate partially rendered posts (no css, js, etc…)
+
+
We could then render the posts twice. One for html rendering and another time for rss. Remark we need to generate the rss version to create the html one.
+
One of the great feature of Hakyll is to be able to save snapshots. Here is how:
Now for each post there is a snapshot named “content” associated. The snapshots are created before applying a template and after applying pandoc. Furthermore feed don’t need a source markdown file. Then we create a new file from no one. Instead of using match, we use create:
Great idea certainly steal from nanoc (my previous blog engine)!
+
Filter the content
+
As I just said, nanoc was my preceding blog engine. It is written in Ruby and as Hakyll, it is quite awesome. And one thing Ruby does more naturally than Haskell is regular expressions. I had a lot of filters in nanoc. I lost some because I don’t use them much. But I wanted to keep some. Generally, filtering the content is just a way to apply to the body a function of type String -> String.
+
Also we generally want prefilters (to filter the markdown) and postfilters (to filter the html after the pandoc compilation).
It will search for all string starting by ‘%’ and it will search in the Map if there is a corresponding abbreviation. If there is one, we replace the content. Otherwise we do nothing.
Generally I write my post in English and French. And this is more difficult than it appears. For example, I need to filter the language in order to get the right list of posts. I also use some words in the templates and I want them to be translated.
The full code is here. And except from the main file, I use literate Haskell. This way the code should be easier to understand.
+
If you want to know why I switched from nanoc:
+
My preceding nanoc website was a bit too messy. So much in fact, that the dependency system recompiled the entire website for any change.
+
So I had to do something about it. I had two choices:
+
+
Correct my old code (in Ruby)
+
Duplicate the core functionalities with Hakyll (in Haskell)
+
+
I added too much functionalities in my nanoc system. Starting from scratch (almost) remove efficiently a lot of unused crap.
+
So far I am very happy with the switch. A complete build is about 4x faster. I didn’t broke the dependency system this time. As soon as I modify and save the markdown source, I can reload the page in the browser.
+
I removed a lot of feature thought. Some of them will be difficult to achieve with Hakyll. A typical example:
Nous pouvons aussi ajouter ces métadonnées dans un fichier externe (toto.md.metadata).↩
+
+]]>
+
+
+ Être correct avec les boutons share
+
+ http://yannesposito.com/Scratch/fr/blog/Social-link-the-right-way/index.html
+ 2013-03-14T00:00:00Z
+ 2013-03-14T00:00:00Z
+
+
+
+
+
tlpl: Les boutons des réseaux sociaux traquent vos utilisateurs, ont un design incohérent avec celui de votre site, utilisent des ressources, ralentissent le rendu de vos pages.
+
Faite les choses bien. Utilisez des liens statiques.
+
Si vous n’avez pas envie de lire, copiez et collez simplement le code suivant dans votre html :
Ever been on a website and want to tweet about it? Fortunately, the website might have a button to help you. But do you really know what this button do?
+
The “Like”, “Tweet” and “+1” buttons will call a javascript. It will get access to your cookies. It helps the provider of the button to know who you are.
+
In plain English, the “+1” button will inform Google you are visiting the website, even if you don’t click on “+1”. The same is true for the “like” button for facebook and the “tweet this” button for twitter.
+
The problem is not only a privacy issue. In fact (sadly imho) this isn’t an issue for most people. These button consume computer ressources. Far more than a simple link. It thus slow down a bit the computer and consume energy. These button could also slow down the rendering of your web page.
+
Another aspect is their design. Their look and feel is mostly imposed by the provider.
+
The most problematic aspect in my opinion is to use a third party js on your website. What if tomorrow twitter update their tweet button? If the upgrade break something for only a minority of people, they won’t fix it. This could occur anytime without any notification. They just have to add a document.write in their js you call asynchronously and BAM! Your website is just an empty blank page. And as you call many external ressources, it can be very difficult to find the origin of the problem.
+
Using social network buttons:
+
+
Pros:
+
+
help user share your website,
+
can provide a popularity indicator to your users.
+
+
Cons:
+
+
you help tracking your users,
+
generally doesn’t follow the design of your website,
+
use more computer ressources,
+
slow down your website,
+
executing third party js can break things silently.
+
+
+
Solutions
+
I will provide you two solutions with the following properties:
+
+
Pros:
+
+
help user share your website,
+
doesn’t follow your user,
+
use almost no computer ressource,
+
doesn’t slow down your website,
+
doesn’t execute any third party js on your website.
You use less computer ressources and more generally power ressources which is good for the planet,
+
Your web pages will load faster.
+
+
ps: On my personal website I continue to use Google analytics. Therefore, Google (and only Google, not facebook nor twitter) can track you here. But I might change this in the future.
Sometimes, the type determine a lot about the function★:
+
fst :: (a,b) -> a -- Only one choice
+snd :: (a,b) -> b -- Only one choice
+f :: a -> [a] -- Many choices
+-- Possibilities: f x=[], or [x], or [x,x] or [x,...,x]
+
+? :: [a] -> [a] -- Many choices
+-- can only rearrange: duplicate/remove/reorder elements
+-- for example: the type of addOne isn't [a] -> [a]
+addOne l = map (+1) l
+-- The (+1) force 'a' to be a Num.
The couple (F,fmap) is a \(\Hask\)'s functor if for any x :: F a:
+
fmap id x = x
+
fmap (f.g) x= (fmap f . fmap g) x
+
+
+
+
Haskell Functors Example: Maybe
+
+
data Maybe a = Just a | Nothing
+instance Functor Maybe where
+ fmap :: (a -> b) -> (Maybe a -> Maybe b)
+ fmap f (Just a) = Just (f a)
+ fmap f Nothing = Nothing
+
fmap (+1) (Just 1) == Just 2
+fmap (+1) Nothing == Nothing
+fmap head (Just [1,2,3]) == Just 1
+
+
+
Haskell Functors Example: List
+
+
instance Functor ([]) where
+ fmap :: (a -> b) -> [a] -> [b]
+ fmap = map
abusing notations denoting join by ⊙; this is equivalent to (F ⊙ F) ⊙ F = F ⊙ (F ⊙ F)
+
There exists η :: a -> F a s.t. η⊙F=F=F⊙η
+
+
+
+
Klesli composition
+
Now the composition works as expected. In Haskell ◎ is <=< in Control.Monad.
+
g <=< f = \x -> join ((fmap g) (f x))
+
f x = [x] ⇒ f 1 = [1] ⇒ (f <=< f) 1 = [1] ✓
+g x = [x+1] ⇒ g 1 = [2] ⇒ (g <=< g) 1 = [3] ✓
+h x = [x+1,x*3] ⇒ h 1 = [2,3] ⇒ (h <=< h) 1 = [3,6,4,9] ✓
+
+
+
+
We reinvented Monads!
+
A monad is a triplet (M,⊙,η) where
+
+
\(M\) an Endofunctor (to type a associate M a)
+
\(⊙:M×M→M\) a nat. trans. (i.e. ⊙::M (M a) → M a ; join)
+
\(η:I→M\) a nat. trans. (\(I\) identity functor ; η::a → M a)
+
+
Satisfying
+
+
\(M ⊙ (M ⊙ M) = (M ⊙ M) ⊙ M\)
+
\(η ⊙ M = M = M ⊙ η\)
+
+
+
+
Compare with Monoid
+
A Monoid is a triplet \((E,∙,e)\) s.t.
+
+
\(E\) a set
+
\(∙:E×E→E\)
+
\(e:1→E\)
+
+
Satisfying
+
+
\(x∙(y∙z) = (x∙y)∙z, ∀x,y,z∈E\)
+
\(e∙x = x = x∙e, ∀x∈E\)
+
+
+
+
Monads are just Monoids
+
+
A Monad is just a monoid in the category of endofunctors, what's the problem?
+
+
The real sentence was:
+
+
All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
+
+
+
+
Example: List
+
+
[] :: * -> * an Endofunctor
+
\(⊙:M×M→M\) a nat. trans. (join :: M (M a) -> M a)
+
\(η:I→M\) a nat. trans.
+
+
-- In Haskell ⊙ is "join" in "Control.Monad"
+join :: [[a]] -> [a]
+join = concat
+
+-- In Haskell the "return" function (unfortunate name)
+η :: a -> [a]
+η x = [x]
A LOT of monad tutorial on the net. Just one example; the State Monad
+
DrawScene to State Screen DrawScene ; still pure.
+
main = drawImage (width,height)
+
+drawImage :: Screen -> DrawScene
+drawImage screen = do
+ drawPoint p screen
+ drawCircle c screen
+ drawRectangle r screen
+
+drawPoint point screen = ...
+drawCircle circle screen = ...
+drawRectangle rectangle screen = ...
+
main = do
+ put (Screen 1024 768)
+ drawImage
+
+drawImage :: State Screen DrawScene
+drawImage = do
+ drawPoint p
+ drawCircle c
+ drawRectangle r
+
+drawPoint :: Point ->
+ State Screen DrawScene
+drawPoint p = do
+ Screen width height <- get
+ ...
+
+
+
fold
+
+
+
+
κατα-morphism
+
+
+
+
κατα-morphism: fold generalization
+
acc type of the "accumulator": fold :: (acc -> a -> acc) -> acc -> [a] -> acc
Algebra representing the (+1) and also knowing about the 0.
+
+
First example, make length on [Char]
+
+
+
κατα-morphism: Type work
+
+data StrF a = Cons Char a | Nil
+data Str' = StrF Str'
+
+-- generalize the construction of Str to other datatype
+-- Mu: type fixed point
+-- Mu :: (* -> *) -> *
+
+data Mu f = InF { outF :: f (Mu f) }
+data Str = Mu StrF
+
+-- Example
+foo=InF { outF = Cons 'f'
+ (InF { outF = Cons 'o'
+ (InF { outF = Cons 'o'
+ (InF { outF = Nil })})})}
+
+
+
+
κατα-morphism: missing information retrieved
+
type Algebra f a = f a -> a
+instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+
cata :: Functor f => Algebra f a -> Mu f -> a
+cata f = f . fmap (cata f) . outF
+
+
+
+
κατα-morphism: Finally length
+
All needed information for making length.
+
instance Functor (StrF a) =
+ fmap f (Cons c x) = Cons c (f x)
+ fmap _ Nil = Nil
+
+length' :: Str -> Int
+length' = cata phi where
+ phi :: Algebra StrF Int -- StrF Int -> Int
+ phi (Cons a b) = 1 + b
+ phi Nil = 0
+
+main = do
+ l <- length' $ stringToStr "Toto"
+ ...
+
+
+
κατα-morphism: extension to Trees
+
Once you get the trick, it is easy to extent to most Functor.
+
type Tree = Mu TreeF
+data TreeF x = Node Int [x]
+
+instance Functor TreeF where
+ fmap f (Node e xs) = Node e (fmap f xs)
+
+depth = cata phi where
+ phi :: Algebra TreeF Int -- TreeF Int -> Int
+ phi (Node x sons) = 1 + foldr max 0 sons
+
+
+
Conclusion
+
Category Theory oriented Programming:
+
+
Focus on the type and operators
+
Extreme generalisation
+
Better modularity
+
Better control through properties of types
+
+
No cat were harmed in the making of this presentation.
tlpl: Mon avis succinct et hautement subjectif concernant les différents languages de programmation que j’ai utilisé.
+
+
BASIC
+
+
+
+
Ah ! Le language de mes premiers programmes ! Je devais avoir 10-11 ans. Sous MO5, Amstrad CPC 6128 et même Atari STe. Le langage des GOTOs. Je suis empleint de nostalgie rien que d’y penser. C’est à peu prêt le seul intérêt de ce langage.
+
Aujourd’hui ce langage est tombé en désuétude. Ce n’est ni un bon langage pour apprendre, ni un bon langage pour faire de vrai programmes. Même si quelques années plus tard, je me remettais à programmer dans un basic avec un compilateur qui pourrait lui redonner vie. Je m’en était servi pour faire un livre dont vous êtes le héro :-).
Toujours lors que j’avais 10 ans, on pouvait faire de petits programmes sympathiques.
+
Je me souviens que lors du chargement de l’application logo on avait droit à de la musique de Bach.
+
Oui, il fallait charger le programme en mémoire avec une cassette. Et elle ne faisait pas les ‘Krrrkrr csssss krrrr’.
+
Je l’avais utilisé sans les boucles. Des années plus tard, je le réutiliser pour faire de l’initiation à l’informatique à mes étudiants de DEUG MIAS première année. Il s’est en fait révélé très utile. Grace à lui, faire des fractales se révèle être un jeu d’enfant, au sens litéral. Je ne peux que conseiller ce langage pour apprendre à programmer et aussi pour le fun.
+
Voici un exemple de code et le résultat est la jolie fractale ‘dragon’.
J’ai dû apprendre à programmer en Pascal aux alentour de 15 ans et je l’ai aussi réutiliser un peit peu en faculté. Je dois avouer, que je le trouve inférieur au C en tous points. J’ai fait pas mal de chose avec ça, comme des algorithmes de graphes, des algorithmes de tri, et même un peu d’intelligence artificielle comme des algorithmes génétiques. Mais je préfère largement le C.
+
C
+
+
+
+
Le langage des pointeurs
+
Ah, le langage de programmation par excellence.
+
Une fois que vous avez compris les boucles et la récursivité. Il est temps de passer aux choses sérieuses. Si vous voulez avoir du code de bonne qualité, alors apprendre le C est quasi-obligatoire.
+
Ce langage est très proche du langage machine. En particulier, (la majorité du temps). Il y a une relation linéaire entre la taille du code en C et de son résultat compilé en assembleur.
+
Ça signifie qu’à chaque fois que vous écrivez une ligne de C, il ne va pas se passer de choses toutes bizarres comme lancer un algorithme qui va prendre deux plombes.
+
Il est très proche de la machine tout en ayant une abstraction suffisante pour ne pas être “trop” désagréable.
+
J’ai fait beaucoup de choses avec. Tous les algorithmes de tri, des algorithmes d’intelligence artificielle (résolution de SAT3), du système, du réseau etc… Bref il est versatile, et on ne peut pas dire que l’on sait programmer si on ne s’est jamais mis à programmer sérieusement en C.
+
ADA
+
Le langage “super propre”.
+
J’avais bien aimé ADA, mais j’avoue que ça n’a duré que le temps d’un semestre de cours. Peut-être qu’un jour je m’y remettrai. Disons qu’il est assez vieux et qu’il a inspiré la plupart des concepts objets.
+
Les langages orientés objets
+
Bon, oui, le Pascal, le C, le Basic (fortran, Cobol et autres) étaient tous des langages impératifs, sans notion d’objets.
+
En gros, il n’y avait pas d’aide pour structurer votre code.
+
Alors, pour aider à limiter le nombre de bug, en particulier pour la création de très gros programmes, on s’est mis à réfléchir à la meilleure façon d’organiser du code d’ordinateur. À la fin, ça à donné la programmation orienté objet. Et donc les langages comme le C manquaient de système pour aider au développement orienté objet. Attention, la programmaiton orienté objet n’est pas la panacée. Combien de programme utilisez-vous qui n’ont pas de bug ? Et ça ne convient pas à tous les type de problème. Mais pour faire une application banquaire, un système de gestion des stocks, des clients ou des archives. C’est-à-dire un système d’information, c’est pas trop mal.
+
Donc les langages orientés objets se sont mis à fleurir.
+
C++
+
+
+
+
Le malpropre
+
en:
+
Et oui l’industrie voulait un langage objet, mais elle n’était pas prête à mettre à la poubelle tout ses codes en C. La solution, prendre C et lui rajouter une couche objet. Le problème avec C++ c’est qu’il fait trop de choses. L’héritage multiple, des templates, etc… Bon, je l’ai quand même choisi pour faire le plus gros programme que j’ai jamais fais lors de ma thèse. Et je dois avouer que l’expérience m’a plu. Le seul reproche que j’ai à faire, c’est que la STL n’était pas aussi complète que l’on aurait pu l’espérer pour un détail. On ne peut pas faire de String<T> pour autre chose que des char16. Du coup, mon alphabet était limité à 216 lettres. Hors, pour certaines application, l’alphabet doit être gigantesque. fr: En conclusion je dirai que C++ est un très bon langage si vous vous fixez à l’avance un sous ensemble de ses fonctionnalités.
+
Eiffel
+
+
+
+
Eiffel est un très beau langage objet. Bien plus propre que C++. Mais, à moins que les choses aient changées, il n’est pas très populaire. Derrière lui il n’a pas la communauté de C++. Pour être franc, j’ai préféré travailler en C++. J’ai menti à mes profs de l’époque pour leur faire plaisir. Lorsqu’on viens du C, il est désagréable de changer ses habitudes.
+
Java
+
+
+
+
On continue vers les langages objets. Alors, à une époque où j’en ai entendu parler, c’était le Graal !
+
La portabilité, votre programme marchera partout. Il était orienté objet. Incrusté à l’intérieur il y avait des concepts d’architecture qui empêchent de faire n’importe quoi… Sauf que.
+
Sauf qu’il est incroyablement verbeux. Et que les limitations sont très désagréables si on sait ce que l’on fait.
+
Par exemple, il n’y a pas d’héritage multiple en Java. Ce qui est en général un choix que je trouve cohérent s’il est bien appuyé par des systèmes qui compensent ce manque. En java, il existe les interfaces. Les interfaces permettent d’ajouter des méthodes à une classe. En aucun cas on ne peut rajouter un attribut autrement qu’en héritant. Cet état de fait m’a vraiment géné.
+
Typiquement je faisais une GUI en Java Swing. J’avais créé mon propre système de notification entre objets. Au début je considérais qu’un objet ne devait envoyer des notifications qu’à un seul objet. Ô quelle erreur lorsque je réalisais qu’il fallait non plus gérer un seul objet mais parfois plusieurs. Je changeais mon implémentation d’interface partout, conséquence, des copier/coller dans tous les sens pour mes classes. Les copier/coller qui sont justement un problème censé être évité par les langages orientés objets.
+
De plus toujours pour ma GUI, je devais évidemment gérer des threads. Hors, il m’a fallu faire mon propre système de gestion de threads pour éviter les locks, pour les notifications (ce thread à fini, etc…). À l’époque j’utilisais Java 1.5. Normallement ce problème devait être réglé sur Java 1.6. J’espère que c’est le cas, mais avoir ce type de “feature” essentielle oubliée par le langage était assez grave.
+
De même, il a fallu attendre très longtemps avant d’avoir des boucles foreach qui rendent le code bien plus lisible.
+
Bon, après cette expérience je déconseillerai Java. La portabilité, n’est pas si intéressante que ce qu’on pourrait croire.
+
En ce qui concerne les GUI, portable signifie interface fonctionnelle mais médiocre sur toutes les plateformes. Quelque soit le système d’ailleurs (wxWidget, QT, etc…). Donc, pour des applications à distribuer à des tiers, c’est à éviter.
+
Le système de Java est très clos. Par contre il résout un très bon problème. Il permet à des développeurs médiocres de travailler en groupe sans faire trop de mal. Et un bon programmeur sera tout de même capable d’y faire des choses très intéressantes. Veuillez noter que je n’ai pas dit que les programmeurs Java sont de mauvais programmeurs, ce n’est pas ce que je pense.
+
Objective-C
+
+
+
+
Le langage que je n’ai appris et utilisé que pour faire des applications sur les plateformes d’Apple(c). J’ai appris Objective-C après Python. Et je dois avouer que j’ai eu du mal à m’y mettre. Je n’ai pas du tout aimé la syntaxe et pas mal d’autres détails. Mais ça fait parti de ces langages que plus on utilise, plus on aime. En réalité, il y a quelque chose dans ce langage qui fait que tout est bien pensé. Mais surtout, ici, ce n’est pas le langage qui est la meilleure partie, c’est plutôt le framework Cocoa qui lui est le plus souvent associé qui est une merveille. Par rapport à tous les autres framework permettant de fabriquer des GUI, Cocoa est de très loin supérieur. Même si ça semble être des détails sur le papier, en pratique cela fait une grande différence.
+
Vraiment jusqu’ici, même si Objective-C reste assez bas niveau, le fait que le typage de ce langage soit dynamique est un vrai plus pour l’interface graphique. Je ne peux que vous encourager à vous accrocher à ce langage et de faire un vrai programme avec. Vous en serez certainement plus ravi qu’il n’y parrait eu début.
+
Les langages interprétés modernes
+
PHP
+
+
+
+
Le petit langage de script que nous utilisions tous pour faire des sites web à l’époque des gifs animées !
+
Sympatique, mais sans plus. Apparemment il y a eu pas mal de progrès depuis PHP5, un jour peut-être que j’y reviendrai. Mais, il a derrière lui une réputation de langage pour les “scripts kiddies”. En gros ceux qui ne savent pas coder. Des trous de sécurité de tous les cotés, etc…
+
En réalité, PHP est au niveau d’abstration à peine supérieur au C. Et donc, il est beaucoup moins bien organisé que des langages objets, favorisant ainsi la création de bug. Pour les applications web, c’est un vrai problème.
+
PHP, reste pour moi le langage de l’injection SQL. J’en fait encore un peu de temps en temps. Et j’ai moi-même dû protéger les accès au SQL pour éviter les injections. Oui, je n’ai pas trouvé de librairie toute prête pour protéger les entrées SQL. Je n’ai pas beaucoup cherché non plus.
+
Python
+
+
+
+
Alors là, attention ! Révélation !
+
Lorsqu’on avait l’habitude de travailler avec des langages compilé, type C++, Java et qu’on passe à Python, on se prend une claque magistrale. La programmation comme elle doit être faite. Tout est si naturel, c’est magique. Oui, c’est si bien que ça. Mais quelque chose d’aussi incroyablement bien doit avoir des inconvénients me dirais-vous.
+
Et bien, oui, comme tous les langages de scripts de haut niveau, Python est lent. Attention pas juste un peu lent, comme 2 fois plus lent que du C. Non, de l’ordre de 10 à 20 fois plus lent que le C. Argh… Bon ça reste utilisable pour beaucoup de choses. Mais certaines application lui sont donc interdites.
+
Awk
+
Des filtres de fichiers à faire. Si ce n’est pas trop compliqué, c’est le langage idéal. Vous avez un fichier et vous voulez savoir quels sont les mots les plus utilisés. Savoir combien de fois un mot est utilisé. Filtrer sous des condition un peu plus compliquées qu’un grep. Super outils. Je l’ai utilisé pour modifier en masse des centaines de fichier XML plus facilement qu’avec du XSLT.
+
Perl
+
Perl c’est assez magique, mais la syntaxe est tellement désagréable à lire que personne ne peut vraiment aimer programmer dans un environnement de plusieurs personnes en Perl. A moins que tous les autres soient des cadors du Perl. Mais la feature qui tue, les expressions régulières :
Va remplacer toto par titi dans la valeur de la variable $var. Et oui, les expressions régulière y sont intégrées directement comme avec sed et awk. Et ça rend le code beacoup plus compact (et parfois illisible). Mais c’est vraiment pas mal. C’est une sorte de awk sous stéroides.
+
Ruby
+
C’est une sorte de Perl en plus propre. Un mélange de Perl et de Python. Les notion objets y sont plus fortes qu’en Python. Je l’ai beaucoup utilisé, je reste quand même un Pythoniste de préférence. Mais Ruby est vraiment très bien. Par contre en terme d’efficacité, c’est le pire langage utilisé par beaucoup de monde de ce point de vue. C’est le langage qui perd quasiment tous les benchmarks. Par contre c’est un outil parfait pour faire des prototypes. Et si vous voulez faire un prototype de site web, RoR est ce qui se fait de mieux. De l’idée au site, il ne se passera que peu de temps.
+
Javascript
+
C’est la bonne surprise. Pendant des années, javascript était considéré comme un langage tout bon à vous embéter dans votre navigation web. En réalité, javascript possède beaucoup de qualité des langages de haut niveau. En particulier, il est facille de passer une fonction en paramèter ou de créer des fonctions anonymes (closures). Récemment, il est devenu très rapide et beaucoup de frameworks et de librairies naissent un peu partout.
+
+
Il y a Cappuccino, Objective-J (comme de l’objective-C mais avec du javascript)
+
Sproutcore
+
Spine.js
+
Backbone.js
+
jQuery
+
prototype.js
+
+
En particulier avec jQuery, on peut faire des appels chainés, très agréables à utiliser. Comme je le disais, c’est une bonne surprise, javascript a été choisi un peu au hasard lors de la création des navigateurs web comme langage de script. Et il s’avère qu’à part sa syntaxe, tout le reste est bien. Heureusement, en ce qui concerne la syntaxe, on peu pallier à ce problème en utilisant CoffeeScript.
+
Les langages fonctionnels
+
CamL
+
J’ai appris CamL à la fac, j’avais trouvé cette expérience très interressante. J’étais plutôt bon, et j’avais les bonnes intuitions mathématiques qui vont avec la programmation fonctionnelle. Mais je dois avouer que je ne l’ai plus jamais utilisé. Simplement, ce type de langage semble si loin de ce qui se fait pour fabriquer des produits que ça me donnais vraiment l’impression d’être un langage pour chercheurs.
+
Haskell
+
Je suis en train d’apprendre ce langage. Et je dois dire que c’est un vrai plaisir. En général les concepts derrière tous les langages de programmation sont assez limités. Chaque langage y va de son petit lot de nouveau concepts, et en général en une après-midi, c’est appris. Pour haskell, c’est très différent. Je sens bien qu’il va me falloir plusieurs semaines pour maîtriser la bête. Ça doit faire quatre semaines que j’apprend haskell un peut tous les jours et je sais qu’il y a des notions que j’ai juste survollées et qui sont assez incroyables. Les Monades par exemple, est un concept que je n’avais jamais rencontré ailleurs. C’est un super concept. De plus le design du langage en fait un parfait système pour paralléliser les calculs naturellement. haskell sépare la partie “pure” de la partie “impure” de la programmation. À ma connaissance, c’est le seul langage de programmation qui fait ça. Enfin, je prend beaucoup de plaisir à apprendre ce langage. La communauté est aussi très acceuillante. Pas de “L0L! URAN00B!”. Et aussi pas de concession du langage pour devenir populaire. Le langage est bon, voilà tout. Alors qu’en Java et C++, typiquement certain choix ont été fait en dépis du bon sens pour “faire plaisir”.
+
Langages originaux
+
Metapost
+
Metapost est un langage qui permet de programmer des dessins. Le gros plus de metapost, c’est sa capacité de résoudre automatiquement les systèmes d’équations linéaires. Par exemple, si vous écrivez :
Ce deuxième exemple positionne X à l’intersection des deux segments AB et CD. Vous pouvez aussi voir pas mal d’exemples ici. You could see more example there.
+
Cette fonction est très utile. Et à mon avis pas seulement pour afficher des choses. De mon point de vue, les autres langages de programmation devraient penser à rajouter les résolutions automatiques simples.
+
zsh
+
Oui, zsh est un shell. Mais c’est aussi un langage de script très bien adapté aux traitement de fichiers. Je le recommande chaudement. C’est pour l’instant le meilleur shell que j’ai utilisé. Je le préfère au bash.
+
Prolog
+
Je n’ai jamais rien fait de conséquent avec Prolog, mais j’ai adoré l’apprendre et l’utiliser. J’ai eu la chance d’apprendre Prolog par Alain Colmerauer lui-même. C’est un langage qui essaye de résoudre les contraintes autant qu’il le peut pour vous. Il en ressort un impression de magie. On ne fait que décrire ce qu’il faut et on ne donne pas d’ordre. Un peu comme la programmation fonctionnelle mais en beaucoup plus puissant.
+
Les langages à découvrir
+
Il reste encore pas mal de langages et de framework à essayer. Actuellement je pense que je vais passer un moment avec Haskell. Peut-être demain que j’irai apprendre LISP, Scala ou Erlang. Comme je suis plus dans la création de site web, j’irai certainement jeter un coup d’œil à clojure aussi. Et certainement beaucoup d’autres choses.
+
Dites moi si vous avez une autre expérience avec ces langages de programmation. Évidement mes impression sont hautement subjectives. Cependant, j’ai utilisé tous les langages dont j’ai parlé.
+
[STL]: Standard Tempate Library [GUI]: Graphic User Interface
On vous propose de vous abonner au flux RSS. Mais de quoi s’agit il ?
+
Si vous n’êtes pas anglophobe je vous recommande la lecture de what is rss ou encore mieux, de regarder cette vidéo RSS explained.
+
+
Mon explication
+
Il s’agit d’un moyen facile d’agréger dans un seul endroit toutes les mises à jours de tous les sites qui vous intéressent.
+
choisir un client
+
Tout d’abord, il faut choisir un client de flux RSS. Aujourd’hui il existe de nombreux client en ligne. C’est-à-dire des sites web qui vont s’occuper du regroupement. Ces client s’appellent des “aggregator”.
+
Personnellement j’utilise Netvibes. J’en ai essayé vraiment beaucoup, et il reste de loin mon préféré.
+
Évidemment Google propose son client aussi : Google Reader. S’il reste adapté pour les contenus pour lesquels on ne veut rien perdre. Il est moins agréable d’utilisation lorsque l’on s’abonne à des flux qui proposent une vingtaine de nouveaux liens par jour.
+
S’abonner aux flux d’un site
+
Donc une fois que l’on a choisi son client, il suffit pour s’abonner de cliquer sur l’icône d’abonnement. Soit il est bien visible sur la page, soit tout en haut dans la barre des tâches.
+
Récupérer les “news”
+
Ensuite lorsque vous utilisez votre client RSS les nouvelles provenant du blog se mettront à jour. Ainsi, il n’y a plus besoin d’aller sur les sites intéressants pour voir s’il n’y a rien de neuf. Ce sont eux qui vous donne leur dernières nouvelles.
YClock est un économiseur d’écran qui vous donne l’heure.i Il a trois thèmes clair, rouge et noir. Il utilise une base de QuartzComposition + du code objective-C pour la gestion du nombre d’images par seconde.
Une explication rapide du pourquoi il y a des erreurs de validation de mes pages.
+
Je voulais utiliser box-shadows et border-radius
+
J’ai donc préféré avoir un approche pragamatique que dogmatique.
+
Utiliser ces propriétés me fait perdre la validation CSS mais fonctionne très bien avec les navigateurs récents (Safari 4 et Firefox 3.5 au moment de l’écriture de ces lignes)
+
Si vous n’utilisez pas ces navigateur les pages s’affichent correctement mais sans ces effets qui n’ont pour but que d’améliorer l’aspect général de la page.
+
Par contre je suis plutôt un partisant de la validation et c’est pourquoi il y a toujours les liens. Tout valide à l’exception des propriétés commençant par -moz et -webkit.
+ +