From 5a96e41469a069ba812473bd9448eb5ce3ac9cc3 Mon Sep 17 00:00:00 2001 From: "Yann Esposito (Yogsototh)" Date: Sun, 28 Jul 2019 18:44:25 +0200 Subject: [PATCH] save --- .project.el.gpg | Bin 3154 -> 3228 bytes src/archive.org | 6 + src/assets/css/minimalist.css | 120 +- src/assets/favicon.ico | Bin 0 -> 1150 bytes src/demo.org | 36 +- src/drafts/Haskell-the-Hard-Way.org | 3772 +++++++++++++++++++ src/drafts/blog-detoxify-web.org | 39 + src/drafts/sad-text-browsing.org | 13 + src/index.org | 21 +- src/posts/2019-07-04-static-org-publish.org | 122 +- src/posts/project-el/auto-load-project.el | 72 + src/posts/project-el/index.org | 133 + 12 files changed, 4147 insertions(+), 187 deletions(-) create mode 100644 src/assets/favicon.ico create mode 100644 src/drafts/Haskell-the-Hard-Way.org create mode 100644 src/drafts/blog-detoxify-web.org create mode 100644 src/drafts/sad-text-browsing.org create mode 100644 src/posts/project-el/auto-load-project.el create mode 100644 src/posts/project-el/index.org diff --git a/.project.el.gpg b/.project.el.gpg index 76921fbd621a4636694f3a5b5338fb797c42e45a..7a2d5c06b8477bdffa034fec504626932654d8d4 100644 GIT binary patch literal 3228 zcmV;N3}f?!0t^FvF6E%v-!tL?5C3+~SKpP>+stS};D)I?t|d*CEA>j#NC1T!|z1*WV;!1d2AFA6wi;FMVwW zYfeER8_iW=GfT_zJmesGPp^esyZ)rb!hJTjSy>U|1w1jn_y*&x%H_5#AKXdzvzSXS_o z-Q1g?a?12S$9%(=05zeT;ZcrfxyK=4*B8e=suFA%P`AuUO)4_HV3V1RE0@q#AQi?9-lL($K?VrSq zAlGQZyM_x$u0tfDYR&;1UyZ@@&WVWKrEW@$PKdnaQK8=ZjO7ScUE*Z;{v{UA_x89E zuVLjoe0J!(Uh>a0N@Q=*a;>aLM0IB%e-h}K-vMw5E*&+15_O9X%iJ_wB$NNMXu%>& zx3e0X6LkoDB4U-wa#8=%dx=|}#En(Wk|GPTq8)=$kTEy3^{dE)Ep`k=E8tY$iVb^1g zxbVc32NSuu50U`SD;iwR7(x|$`C^bVs5ael(MhHw@ ztBktgtZ5?B)HQc~)2u&HH$`TRc8$knc@~6-^KQz2XcxYZ5`$fPPWOW7gjgcSv-n{a zYm-@2!HZE}jhJp5SJ`PwjJ(bQ-z|6JE|g`$j0GuRH5=KG+-T~mjA8z=T#SYHpt9F# z_k8~0>c7*B(}6H1xe%l0Iw$IxAlaS@Q8dr^zpmz=o7$iK5OEfJCJqrj$Y9u44ZOnVtuaESN8lhXwFsBQ7jQCY0k77 zW?y*^rV6^ce4ecx+kJjHB59nFiD_Z-U1+#k5J`E>T0OVFl5k$2_1A_VQ7({}I0H-i2e=oF;}&4cv-M>N zG%i~jR3zj6yw=M;QT_HNJL{j&;n#Ykfk+To9-lD=^{Oq#-;#zQBEuIV^<`gZGM3p= z-^vwwT}l%z(H^O`_t`o@N2N?BP_dy_IomhdGdWgVk2L@s!6;%wN)Cu;dw$Sr$ptN) zYG;z5I2>L#x{M844=a-CQxev0Jg%cXTWFC-lk^cKxt~eNY>6PgBNZN4^MurB*Xa$% z|7Cq!sV~ib_r#CdFi)Ct^WfvwLH*$q0y>17lOdomO%`ndGDk24`QrY~HUw&4@O-@R zwLp?qiJM}GGI2h}!C}9cD-AF^W3fC(B$?QvU> zyF_3mkwR~qWY}dkvX6HDds+a zml%|U+Lxax;Rq==1FoQSLSFOmvfX1BLY&;V7}ZHZf5|I%0H;CS!ZLQYzz)0WCQ}kPPT@ zCR$)k;LZ?|JuJ={Ft%Ct+Zmbo1&L zoO<$*1*JQL`$TgN{C`3y^baeH3UPyy*2A*4oiKvm%9))3v@=bay6f{v&pOkx^&_v- zH@Lj_5Mb+AlDVE+up0+TCvFI?cM=R!&v6PeB^sYJVmub(G9B*0h=|V*Cd|D-9_m?Y zw4if{*E}|n)oR4}F|?dMsM%oBb-HENzNsorwrisV`;~}=s_F-_?bf_h(_-d&AI}%D z(bfHQaFVbEdhHo!N$I?p+rQ4z?D>_AV1UrSinnfAd@h~J43~X)0v>fW;ArO$rX+aj zqIc|H%5nItnJhVZp&fOCb3F~pdYntYYtal9l1hY0lRLI>AagGYbVwj|0^kpxp1))K zN+k(en{xaqh)>>yD7)AGDs{5x;1aB#^z`qbW_FBr7+)Sm;V-t1(hWyqr>_3J2vCVJ zk&3zNG2l4e%3>ASXGBxWQB+oUm!Z05-G%27s-B{W;7~N2(+YsUc0~XKVTFQO_ph4G zfm7L|MZP7@ifcHK+w{a(8};`otg5}Gh<$jmOVc+^O8|>Vj#m2dv=MH{F7=Nq`xL}h zb|^h^E4}~ZAm5eWtV{-p)O6G zNa#@_vn*rKCw)Vz@y8sv0OR0(zVWAithYXd)#p*F$vg^H25+?j?Lb0xFoG=EL`kFF zT=#zWwXx11#i^HQhK#)~#2FFNS#e2<6GwQuLorq!*FkFb_Zg0yOhC}k1Bx2TGQwy5h?ELsppKWj7Mz1FC`Q5C-GV-4}3w(jO3- zJI?{18$^p^5_qE(qfzf{pZIJC0zniK2C<_rb5$ma0vI$z`&@8bt26K-BE68*tLN%m zyNG1ND(|V#(htDKO7NGywg{n~Jv1t-|9)?eMN)zl%&f;D+)5J(EvqbtGr{zCCm9Cy z|1CX9D-n}iRnd&g4})HE=dCSQXi%H9;7_C}P9)z#0*~Kv6b`%m873u{WlSU*H?%bl zqJ`L>I3uj*xtc7o=o?5tDj2gsY6-78! zTy|`X!rstgiZhbiJ%+HBmY;RrCm~VdmG1Ne^m;%q`}x97n)<+YH5(Lc;S8aNz*V`= z|73QI!Xt(LpZAgQWP2ws^oo!V7en!0XSvi>UIpS*{M4S-;u#&U8{GLbe^kbXRl8I+ zD8@C*(2e@A7YbRxeTov@D5AwG~}k+27$L&l4=3+I6qZdG!^ zIdE=T5@~IKrTs`*zy~4n-~YON%jEg>jK}XA5zhshoNf4zw|x4o&uKX~SlZ@r<^ReC3POofcKvKQ<8zg3~fGY(4Ygk#B^&@0tn_iVuh~Lh(2g-t=Ok{pW zok!!d1B@1g35`|g zPVoM-Y9dDOag40p&m|?%5tT`bo!0N+V@qA_Jd(wh=f9{h#8Bx-rCGvpJfMhdx;*wV zt-XFREj^qA(F6AfWDgEB-?)_?HtgueZ+Fy7*tPQ8cqq8CwB&-ab%Gj)T5%5GW++gF zGg{ffCkLjy)-2p|t0{eiKa_iCm$rQt{uvzL!V{>jj3Ohz zUL;6tNMh|{|LsA0Ios7pe~<3APLV1L;_|%hw#phG8*V1g(+R9+8xv|ErENy>7(OWz zQn;m{SW&&7ih-R5X(lN}8r0-ps2~e&-#ycsozvJ>kbPLI)SvG+t^mQq)icNP7K5Y^kQA>EU({j7*Fv>1kKmE@lRs z@M-)CMro<{79b+et&N-;y4TY6X%a0oDj{IvnxVXxjNr0Uu`n(ZBz?5*YE&`m2;)Mti{Hu5}P}sTHiSI(LAzqo*$YoA&l69-F#>7Dqzk zG?DH@q3>y7H=1rtN_)7RjUTdO1xNQHUk*M=ynZG~Qu|YM4~H`A5k*(gZ5d&fmhJTY z%{R|+`9j44W(&a*3kYVHVopFeaL4mkfQ#ZSyU2B*0sEd2(O|hxhB%8+LVq(1!`x$* zPKh6&H#>@-HXsg#1TUAsTAsTmgH@-f*iRY>xxZ~_;Q#!UxHusnzn=)%f#S_ZPZ9&$ zFj^-uwPaUwvFlij1i0h=40`VqBm8uL4d)9b zVdGABDz~+vWPNB(;jJ4zam!Fw&nXvBtITD<)*N2d#U?QxNn|G`j3|J&O_U!>R^*o8 zP7cR`Pev4D3M8e+SHv*?!Ta$J3k5)NuCGsf(H#Rw#hOvg=db)AiE6yhOztTz zlGqp{HnKUsLoe2_T-#%s$VE!r3mX%&%y8;|cJvixdn3p+oHev?PScOM(Lk`rEl9_BXt>L}1%2r} z0TWOkxcw-(!S^>|A+%(Ltk>S+>pr>I%b8ewcz-Er=M2?t2i%W>tgRzMCCt3P?W@z= zre=7k7zWH~L`o1LIeE1SGekjtsMBn*bGSJ&9UTLMSFF~V9{K5}>=rv0g8;!bz zm97Q+m4$}BE(tlwR7hoWe>V$@fcxohZ)BlbQwJSXa8>`2^LnIBV5eCzWX`L*SC@lC zjTT(xoEc_07$sx-lk4;M{oa#v?5ZdNDpCno~hbY7fEx)T1CKf4SnZ)2A#hS2Qfx5)Z z$o?E|b?FCF!!08_`8gPZPKPHIz<^|~&}>ZR0{SZRxM2;X9B(Sr6J77;NycSn;Yv(P z)rqe_Hz#PyX#n%m(RyIb{EAdSV*o#{8YBChRF~~*XdBgft}8F(qos)T?^eshJaSnC zHV<}kZQ(-IDr*SpK`jAWH-6O2mCj?)al|$5CLGUVZ`|QW@FmX`zWBG#kvuk)Fj~*z z@`7cVejAmoe1SS`K{Hsg4-esU_4EN?05Q1fYcdmc2rxt$6`JN)XKUFz17a zYs<|5_Yp3`d%|_1%~I_0K!<@mU^6w*6Ht_yL4aFqbvv7?twa>LtFR!1|8enl<*iD4 zN$U0ljuDwRoyhT5-U0ZR#X>MjR=ETQ1jv*!CgqPGB;&dgMy z;cXDn$PfI5LAX=leuEfr!lWv76IazUcNqn)w?%*WVq^zUb<0Tg&wq>CwcLdF&gCcYW}_hR$n|$ssLMAFy({wS;Q3L|`qlgH_e2!2GCUQRXJ+ z?StJ*G}oXvcG{cV%sTb28w5pTor?(u69l6nXT#kk{Hm;*=~m@*^;5xNy{-mcdgArM z#6gXEb^vioc1gU2tv2ePFrfElo09wa?;1&ZmiKysw9wH>1hYsSQDmF6sO<0-yeT%5 zRkZv6oKW7bG(WR3x->x}f8X!j&ia~4VsI?vm>uV*y#EDh^CqJyDah+_rQ+CgO>OMMZEM2XR&x z24D$ab3)F`7$#LTuiGY#UrQbPO=K?OZ!LDj_GxfH$uf7--!j~tV7iN+ex9D#7S(FR z?N&eU<0oIkDxH#rN7KwWIR#$9BlX;f4`8ukfHY;Dpv$1#QBFTAk#W)Z(m+Ek6$rEVzDX+36G`6;r1TWd5*=PvKd{ sKE1^zfL})*;K7Ea*N)lO-wykW_1Ny(d@;=GXVVyKHOcuwMDYvgMeL^>{r~^~ diff --git a/src/archive.org b/src/archive.org index b48dfaa..e343752 100644 --- a/src/archive.org +++ b/src/archive.org @@ -2,6 +2,12 @@ #+AUTHOR: Yann Esposito #+EMAIL: yann.esposito@gmail.com +* Articles + :PROPERTIES: + :ID: A238A3BD-18B5-42D8-8FB3-E522DB3795CC + :PUBDATE: <2019-07-28 Sun 15:11> + :END: #+begin_archive +@@html:
  • @@ @@html:@@ 2019-07-28: @@html:@@ [[file:posts/autoload-emacs-script-by-project.org][Autoload Script by project]] @@html:@@ @@html:
  • @@ @@html:
  • @@ @@html:@@ 2019-07-07: @@html:@@ [[file:posts/2019-07-04-static-org-publish.org][Static blog with org-mode]] @@html:@@ @@html:
  • @@ #+end_archive diff --git a/src/assets/css/minimalist.css b/src/assets/css/minimalist.css index 09d8366..38db000 100644 --- a/src/assets/css/minimalist.css +++ b/src/assets/css/minimalist.css @@ -27,42 +27,14 @@ body { -moz-hyphens:auto; -ms-hyphens:auto; } -#preamble { - border-bottom: solid 1px; -} -#preamble h1 { - margin-bottom: 0; -} -#postamble { - border-top: solid 1px; -} -#preamble,#postamble { - padding: 10px 0; -} -.menu { - opacity: 0.5; -} -.menu:hover, .menu:focus { - opacity:1; -} -#content,.content { - max-width: 50em; - margin: 0 auto; - padding: 10px; -} -#content *:first-child { - margin-top: 0; -} - h1, h2, h3, h4, h5, h6, pre, code, blockquote, ol, ul, ol ol, ul ul, ul ol, ol ul, li, p, section, header, footer { float: none; margin: 0; padding: 0; } - h1, h2, h3, h4, h5, h6, pre, code, blockquote, p, ul, ol, section, header, -.figure { +figure { margin-top: 20px; margin-bottom: 20px; } @@ -152,7 +124,6 @@ blockquote:after { position: absolute; top: 0; left: 0; - opacity: 0.3; } li { position: relative; @@ -176,8 +147,6 @@ ol > li:before { opacity: 0.5; } -/* colors */ - img { max-width: 100%; max-height: 800px; @@ -188,13 +157,6 @@ p > img, li > img { margin: 0; vertical-align: middle; } -footer { - font-size: 0.8em; -} -.article-date { - opacity: 0.5; - font-size: 0.8; -} table { width: 100%; margin: 20px 0; @@ -219,15 +181,32 @@ navigation { navigation > a { margin-right: 10px; } -.footpara { display: inline; } -.footdef > sup { - vertical-align: middle; -} -.footdef > sup::after { - content: ": "; -} -/* org mode statuses */ +/* org mode ids and classes */ +.figure { + margin-top: 20px; + margin-bottom: 20px; +} +#preamble { + border-bottom: solid 1px; +} +#preamble h1, #preamble h2 { + margin: 0; +} +#postamble { + border-top: solid 1px; +} +#preamble,#postamble { + padding: 10px 0; +} +#content,.content { + max-width: 50em; + margin: 0 auto; + padding: 10px; +} +#content *:first-child { + margin-top: 0; +} .timestamp-wrapper { font-size: 12px; } @@ -236,6 +215,21 @@ navigation > a { font-weight: bold; padding: 1px 1ex; } +.article-date { + font-size: 0.8; + font-style: italic; + float: right; +} +.footpara { + display: inline; +} +.footdef > sup { + vertical-align: middle; +} +.footdef > sup::after { + content: ": "; +} + /* colors theme */ :root { @@ -258,7 +252,8 @@ navigation > a { --green: #859900; --transparent: rgba(255,255,255,0); - --main-background: #00151b; /* 0.5 darker than #002b36; */ + /* --main-background: #00151b; /* 0.5 darker than #002b36; */ + --main-background: var(--base03); /* 0.5 darker than #002b36; */ --main-foreground: var(--base1); --second-foreground: var(--base0); --soft-foreground: var(--base01); @@ -279,7 +274,6 @@ navigation > a { --todo-txt: #FFF; } } - body { background: var(--main-background); color: var(--main-foreground); @@ -301,6 +295,12 @@ pre::after,pre::before,hr:after { a:hover, a:active, a:focus { color: var(--yellow); } +navigation a, navigation a:visited { + color: var(--soft-foreground); +} +navigation a:focus, navigation a:hover { + color: var(--yellow); +} thead { background-color: var(--reveal-background); color: var(--second-fg); @@ -308,24 +308,20 @@ thead { tr:hover { background-color: var(--reveal-background); } -#postamble:hover, #preamble:hover { - background-color: var(--reveal-background); - border-color: var(--border-color); -} h1 { - color: var(--violet); -} -h2 { color: var(--magenta); } +h2 { + color: var(--violet); +} h3 { - color: var(--red); + color: var(--blue); } h4 { - color: var(--orange); + color: var(--cyan); } h5 { - color: var(--yellow); + color: var(--green); } h6 { color: var(--green); @@ -336,9 +332,15 @@ table, td, th { code { background: var(--reveal-background); } +blockquote:after { + color: var(--soft-foreground); +} #preamble,#postamble { border-color: var(--transparent); } +.article-date { + color: var(--soft-foreground); +} /* -------- */ /* org colors */ .todo, .done { diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3372902c86c3cb544bd4848db84e094b279d2a38 GIT binary patch literal 1150 zcmbW0O=uHA6vtovNWep_7x8AIJrqjZR9Z~6-EHevY6u0tMbX+|saHioz33smc?pz) z2L%zWAShTocq%A@iYGy@O6mvLMDg0g>-;92WV2QTcX_*S{_p>P^JeCaNQ-&f+QfVl zGOU&$742&2YxM=W_-Z!dJ=TCH)*0d7{h^! z25oWjYK)wJY7-iGo2~g?qA4$2G{wm)F|(#&Q&%NA*A6ex&UC>Vel(5-t=X3c(e6Utoh*Y0_%>&||(h7VS&qp=pWoR8D9UTS7uv>P5( ztl@(ZgX8$_eBJne^effhnwiyAgKso;`hzumu$AhM?q7c@sok0LIxzlP&)j%#4IhkH zz1HRZ3!h*Awxk}(_UW_75A{pAtks2Y*6_iI!6An`KYF0iKS{lNtE9hw{m`G4nwA{+ zV8nbcX?xK1Pq=>vOkdr4e}*s)x7?)g*w{`GS`2R;}v z%L&~dcMrLfmoJXT4-W6j7Ea`6ZWgc2-@84vh~a|~gA=~LpW-5Ot3|H2xJ>{4!$pI3 E30lt1hX4Qo literal 0 HcmV?d00001 diff --git a/src/demo.org b/src/demo.org index 284a2a4..e5f28f5 100644 --- a/src/demo.org +++ b/src/demo.org @@ -36,6 +36,9 @@ There should be a forced newline. It lets you and others work together on projects from anywhere. *** Level 3 + :PROPERTIES: + :CUSTOM_ID: level-3 + :END: #+begin_quote This is a blockquote following a header. @@ -45,6 +48,9 @@ There should be a forced newline. #+end_quote **** Level 4 + :PROPERTIES: + :CUSTOM_ID: level-4 + :END: #+begin_src javascript // Javascript code with syntax highlighting. @@ -78,6 +84,9 @@ There should be a forced newline. #+end_src ***** Level 5 + :PROPERTIES: + :CUSTOM_ID: level-5 + :END: - this is an unordered list following a header. - this is an unordered list following a header. @@ -121,6 +130,9 @@ Bad too wide table... ------ *** Here is an unordered list: + :PROPERTIES: + :CUSTOM_ID: here-is-an-unordered-list- + :END: - level 1 item - level 2 item @@ -140,27 +152,39 @@ Bad too wide table... - level 2 item **** Image test + :PROPERTIES: + :CUSTOM_ID: image-test + :END: an image: #+CAPTION: Testing include an image #+NAME: fig:test-image #+ATTR_HTML: The Experiment -[[../assets/img/a.png]] +[[../img/a.png]] We could try inline image -[[../assets/img/a.png]] +[[../img/a.png]] just to check. -- [[../assets/img/a.png]] item with img - - [[../assets/img/a.png]] item with img - - [[../assets/img/a.png]] item with img - - [[../assets/img/a.png]] item with img +- [[../img/a.png]] item with img + - [[../img/a.png]] item with img + - [[../img/a.png]] item with img + - [[../img/a.png]] item with img ***** TODO todo + :PROPERTIES: + :CUSTOM_ID: todo + :END: ****** IN-PROGRESS in-progress + :PROPERTIES: + :CUSTOM_ID: in-progress + :END: ******* IN-REVIEW in-review + :PROPERTIES: + :CUSTOM_ID: in-review + :END: ****** HOLD on hold state - State "HOLD" from "IN-REVIEW" [2019-07-09 Tue 13:44] \\ some reason diff --git a/src/drafts/Haskell-the-Hard-Way.org b/src/drafts/Haskell-the-Hard-Way.org new file mode 100644 index 0000000..72333ec --- /dev/null +++ b/src/drafts/Haskell-the-Hard-Way.org @@ -0,0 +1,3772 @@ +#+TITLE: Learn Haskell Fast and Hard +#+AUTHOR: Yann Esposito +#+KEYWORDS: Haskell, programming, functional, tutorial +#+OPTIONS: auto-id:t +#+PROPERTY: eval no + +blogimage("magritte_pleasure_principle.jpg","Magritte pleasure +principle") + +%tldr A very short and dense tutorial for learning Haskell. + +Thanks to: + +- [[https://plus.google.com/u/0/113751420744109290534][Oleg Taykalo]] + you can find a Russian translation here: + [[http://habrahabr.ru/post/152889/][Part 1]] /&/ + [[http://habrahabr.ru/post/153383/][Part 2]], +- [[http://silly-bytes.blogspot.fr][Daniel Campoverde]] for the Spanish + translation here: + [[http://silly-bytes.blogspot.fr/2016/06/aprende-haskell-rapido-y-dificil_29.html][Aprende + Haskell rápido y difícil]], +- [[http://github.com/joom][Joomy Korkut]] for the Turkish translation + here: [[https://github.com/joom/zor-yoldan-haskell][Zor Yoldan + Haskell]]. + +I really believe all developers should learn Haskell. I don't think +everyone needs to be super Haskell ninjas, but they should at least +discover what Haskell has to offer. Learning Haskell opens your mind. + +Mainstream languages share the same foundations: + +- variables +- loops +- pointers[fn:1] +- data structures, objects and classes (for most) + +Haskell is very different. The language uses a lot of concepts I had +never heard about before. Many of those concepts will help you become a +better programmer. + +But learning Haskell can be hard. It was for me. In this article I try +to provide what I lacked during my learning. + +This article will certainly be hard to follow. This is on purpose. There +is no shortcut to learning Haskell. It is hard and challenging. But I +believe this is a good thing. It is because it is hard that Haskell is +interesting. + +The conventional method to learning Haskell is to read two books. First +[[http://learnyouahaskell.com]["Learn You a Haskell"]] and just after +[[http://www.realworldhaskell.org]["Real World Haskell"]]. I also +believe this is the right way to go. But to learn what Haskell is all +about, you'll have to read them in detail. + +In contrast, this article is a very brief and dense overview of all +major aspects of Haskell. I also added some information I lacked while I +learned Haskell. + +The article contains five parts: + +- Introduction: a short example to show Haskell can be friendly. +- Basic Haskell: Haskell syntax, and some essential notions. +- Hard Difficulty Part: + + - Functional style; a progressive example, from imperative to + functional style + - Types; types and a standard binary tree example + - Infinite Structure; manipulate an infinite binary tree! + +- Hell Difficulty Part: + + - Deal with IO; A very minimal example + - IO trick explained; the hidden detail I lacked to understand IO + - Monads; incredible how we can generalize + +- Appendix: + + - More on infinite tree; a more math oriented discussion about + infinite trees + +#+BEGIN_QUOTE + Note: Each time you see a separator with a filename ending in =.lhs= + you can click the filename to get this file. If you save the file as + =filename.lhs=, you can run it with + + #+BEGIN_SRC bash + runhaskell filename.lhs + #+END_SRC + + Some might not work, but most will. You should see a link just below. +#+END_QUOTE + +* Introduction + :PROPERTIES: + :CUSTOM_ID: introduction + :END: + +** Install + :PROPERTIES: + :CUSTOM_ID: install + :END: + +blogimage("Haskell-logo.png", "Haskell logo") + +There are different way to install Haskell, I would recommend to use +[[https://haskellstack.org][=stack=]]. + +There are other way to install Haskell on your system you could visit, +you can learn more about it by visiting +[[https://haskell.org][haskell.org]] or +[[https://haskell-lang.org][haskell-lang.org]] + +Tools: + +- =ghc=: Compiler similar to gcc for =C=. +- =ghci=: Interactive Haskell (REPL) +- =runhaskell=: Execute a program without compiling it. Convenient but + very slow compared to compiled programs. + +** Don't be afraid + :PROPERTIES: + :CUSTOM_ID: don't-be-afraid + :END: + +blogimage("munch_TheScream.jpg","The Scream") + +Many books/articles about Haskell start by introducing some esoteric +formula (quick sort, Fibonacci, etc...). I will do the exact opposite. +At first I won't show you any Haskell super power. I will start with +similarities between Haskell and other programming languages. Let's jump +to the mandatory "Hello World". + +#+BEGIN_SRC haskell :eval never-export + main = putStrLn "Hello World!" +#+END_SRC + +To run it, you can save this code in a =hello.hs= and: + +#+BEGIN_EXAMPLE + ~ runhaskell ./hello.hs + Hello World! +#+END_EXAMPLE + +or if you use =stack= first run =stack setup= and then: + +#+BEGIN_EXAMPLE + ~ stack runhaskell ./hello.hs + Hello World! +#+END_EXAMPLE + +You could also download the literate Haskell source. You should see a +link just above the introduction title. Download this file as +=00_hello_world.lhs= and: + +#+BEGIN_EXAMPLE + ~ runhaskell 00_hello_world.lhs + Hello World! +#+END_EXAMPLE + +01_basic/10_Introduction/00_hello_world.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/10_hello_you.lhs + +Now, a program asking your name and replying "Hello" using the name you +entered: + +#+BEGIN_SRC haskell :eval never-export + main = do + print "What is your name?" + name <- getLine + print ("Hello " ++ name ++ "!") +#+END_SRC + +First, let us compare this with similar programs in a few imperative +languages: + +#+BEGIN_SRC python + # Python + print "What is your name?" + name = raw_input() + print "Hello %s!" % name +#+END_SRC + +#+BEGIN_SRC ruby + # Ruby + puts "What is your name?" + name = gets.chomp + puts "Hello #{name}!" +#+END_SRC + +#+BEGIN_SRC C + // In C + #include + int main (int argc, char **argv) { + char name[666]; // <- An Evil Number! + // What if my name is more than 665 character long? + printf("What is your name?\n"); + scanf("%s", name); + printf("Hello %s!\n", name); + return 0; + } +#+END_SRC + +The structure is the same, but there are some syntax differences. The +main part of this tutorial will be dedicated to explaining why. + +In Haskell there is a =main= function and every object has a type. The +type of =main= is =IO ()=. This means =main= will cause side effects. + +Just remember that Haskell can look a lot like mainstream imperative +languages. + +01_basic/10_Introduction/10_hello_you.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/20_very_basic.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("picasso_owl.jpg","Picasso minimal owl") + +Before continuing you need to be warned about some essential properties +of Haskell. + +/Functional/ + +Haskell is a functional language. If you have an imperative language +background, you'll have to learn a lot of new things. Hopefully many of +these new concepts will help you to program even in imperative +languages. + +/Smart Static Typing/ + +Instead of being in your way like in =C=, =C++= or =Java=, the type +system is here to help you. + +/Purity/ + +Generally your functions won't modify anything in the outside world. +This means they can't modify the value of a variable, can't get user +input, can't write on the screen, can't launch a missile. On the other +hand, parallelism will be very easy to achieve. Haskell makes it clear +where effects occur and where your code is pure. Also, it will be far +easier to reason about your program. Most bugs will be prevented in the +pure parts of your program. + +Furthermore, pure functions follow a fundamental law in Haskell: + +#+BEGIN_QUOTE + Applying a function with the same parameters always returns the same + value. +#+END_QUOTE + +/Laziness/ + +Laziness by default is a very uncommon language design. By default, +Haskell evaluates something only when it is needed. In consequence, it +provides a very elegant way to manipulate infinite structures, for +example. + +A last warning about how you should read Haskell code. For me, it is +like reading scientific papers. Some parts are very clear, but when you +see a formula, just focus and read slower. Also, while learning Haskell, +it /really/ doesn't matter much if you don't understand syntax details. +If you meet a =>>==, =<$>=, =<-= or any other weird symbol, just ignore +them and follows the flow of the code. + +#+BEGIN_HTML +

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

    +#+END_HTML + +You might be used to declaring functions like this: + +In =C=: + +#+BEGIN_SRC C + int f(int x, int y) { + return x*x + y*y; + } +#+END_SRC + +In JavaScript: + +#+BEGIN_EXAMPLE + function f(x,y) { + return x*x + y*y; + } +#+END_EXAMPLE + +in Python: + +#+BEGIN_SRC python + def f(x,y): + return x*x + y*y +#+END_SRC + +in Ruby: + +#+BEGIN_SRC ruby + def f(x,y) + x*x + y*y + end +#+END_SRC + +In Scheme: + +#+BEGIN_SRC scheme + (define (f x y) + (+ (* x x) (* y y))) +#+END_SRC + +Finally, the Haskell way is: + +#+BEGIN_SRC haskell :eval never-export + f x y = x*x + y*y +#+END_SRC + +Very clean. No parenthesis, no =def=. + +Don't forget, Haskell uses functions and types a lot. It is thus very +easy to define them. The syntax was particularly well thought out for +these objects. + +#+BEGIN_HTML +

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

    +#+END_HTML + +Although it is not mandatory, type information for functions is usually +made explicit. It's not mandatory because the compiler is smart enough +to discover it for you. It's a good idea because it indicates intent and +understanding. + +Let's play a little. We declare the type using =::= + +#+BEGIN_SRC haskell :eval never-export + f :: Int -> Int -> Int + f x y = x*x + y*y + + main = print (f 2 3) +#+END_SRC + +#+BEGIN_EXAMPLE + ~ runhaskell 20_very_basic.lhs + 13 +#+END_EXAMPLE + +01_basic/10_Introduction/20_very_basic.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/21_very_basic.lhs + +Now try + +#+BEGIN_SRC haskell :eval never-export + f :: Int -> Int -> Int + f x y = x*x + y*y + + main = print (f 2.3 4.2) +#+END_SRC + +You should get this error: + +#+BEGIN_EXAMPLE + 21_very_basic.lhs:6:23: + No instance for (Fractional Int) + arising from the literal `4.2' + Possible fix: add an instance declaration for (Fractional Int) + In the second argument of `f', namely `4.2' + In the first argument of `print', namely `(f 2.3 4.2)' + In the expression: print (f 2.3 4.2) +#+END_EXAMPLE + +The problem: =4.2= isn't an Int. + +01_basic/10_Introduction/21_very_basic.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/22_very_basic.lhs + +The solution: don't declare a type for =f= for the moment and let +Haskell infer the most general type for us: + +#+BEGIN_SRC haskell :eval never-export + f x y = x*x + y*y + + main = print (f 2.3 4.2) +#+END_SRC + +It works! Luckily, we don't have to declare a new function for every +single type. For example, in =C=, you'll have to declare a function for +=int=, for =float=, for =long=, for =double=, etc... + +But, what type should we declare? To discover the type Haskell has found +for us, just launch ghci: + +#+BEGIN_HTML +
    
    +  % ghci
    +  GHCi, version 7.0.4: http://www.haskell.org/ghc/  :? for help
    +  Loading package ghc-prim ... linking ... done.
    +  Loading package integer-gmp ... linking ... done.
    +  Loading package base ... linking ... done.
    +  Loading package ffi-1.0 ... linking ... done.
    +  Prelude> let f x y = x*x + y*y
    +  Prelude> :type f
    +  f :: Num a => a -> a -> a
    +  
    +#+END_HTML + +Uh? What is this strange type? + +#+BEGIN_EXAMPLE + Num a => a -> a -> a +#+END_EXAMPLE + +First, let's focus on the right part =a -> a -> a=. To understand it, +just look at a list of progressive examples: + +| The written type | Its meaning | +|--------------------+---------------------------------------------------------------------------| +| =Int= | the type =Int= | +| =Int -> Int= | the type function from =Int= to =Int= | +| =Float -> Int= | the type function from =Float= to =Int= | +| =a -> Int= | the type function from any type to =Int= | +| =a -> a= | the type function from any type =a= to the same type =a= | +| =a -> a -> a= | the type function of two arguments of any type =a= to the same type =a= | + +In the type =a -> a -> a=, the letter =a= is a /type variable/. It means +=f= is a function with two arguments and both arguments and the result +have the same type. The type variable =a= could take many different type +values. For example =Int=, =Integer=, =Float=... + +So instead of having a forced type like in =C= and having to declare a +function for =int=, =long=, =float=, =double=, etc., we declare only one +function like in a dynamically typed language. + +This is sometimes called parametric polymorphism. It's also called +having your cake and eating it too. + +Generally =a= can be any type, for example a =String= or an =Int=, but +also more complex types, like =Trees=, other functions, etc. But here +our type is prefixed with =Num a =>=. + +=Num= is a /type class/. A type class can be understood as a set of +types. =Num= contains only types which behave like numbers. More +precisely, =Num= is class containing types which implement a specific +list of functions, and in particular =(+)= and =(*)=. + +Type classes are a very powerful language construct. We can do some +incredibly powerful stuff with this. More on this later. + +Finally, =Num a => a -> a -> a= means: + +Let =a= be a type belonging to the =Num= type class. This is a function +from type =a= to (=a -> a=). + +Yes, strange. In fact, in Haskell no function really has two arguments. +Instead all functions have only one argument. But we will note that +taking two arguments is equivalent to taking one argument and returning +a function taking the second argument as a parameter. + +More precisely =f 3 4= is equivalent to =(f 3) 4=. Note =f 3= is a +function: + +#+BEGIN_EXAMPLE + f :: Num a => a -> a -> a + + g :: Num a => a -> a + g = f 3 + + g y ⇔ 3*3 + y*y +#+END_EXAMPLE + +Another notation exists for functions. The lambda notation allows us to +create functions without assigning them a name. We call them anonymous +functions. We could also have written: + +#+BEGIN_EXAMPLE + g = \y -> 3*3 + y*y +#+END_EXAMPLE + +The =\= is used because it looks like =λ= and is ASCII. + +If you are not used to functional programming your brain should be +starting to heat up. It is time to make a real application. + +01_basic/10_Introduction/22_very_basic.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/23_very_basic.lhs + +But just before that, we should verify the type system works as +expected: + +#+BEGIN_SRC haskell :eval never-export + f :: Num a => a -> a -> a + f x y = x*x + y*y + + main = print (f 3 2.4) +#+END_SRC + +It works, because, =3= is a valid representation both for Fractional +numbers like Float and for Integer. As =2.4= is a Fractional number, =3= +is then interpreted as being also a Fractional number. + +01_basic/10_Introduction/23_very_basic.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/10_Introduction/24_very_basic.lhs + +If we force our function to work with different types, it will fail: + +#+BEGIN_SRC haskell :eval never-export + f :: Num a => a -> a -> a + f x y = x*x + y*y + + x :: Int + x = 3 + y :: Float + y = 2.4 + -- won't work because type x ≠ type y + main = print (f x y) +#+END_SRC + +The compiler complains. The two parameters must have the same type. + +If you believe that this is a bad idea, and that the compiler should +make the transformation from one type to another for you, you should +really watch this great (and funny) video: +[[https://www.destroyallsoftware.com/talks/wat][WAT]] + +01_basic/10_Introduction/24_very_basic.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("kandinsky_gugg.jpg","Kandinsky Gugg") + +I suggest that you skim this part. Think of it as a reference. Haskell +has a lot of features. A lot of information is missing here. Come back +here if the notation feels strange. + +I use the =⇔= symbol to state that two expression are equivalent. It is +a meta notation, =⇔= does not exists in Haskell. I will also use =⇒= to +show what the return value of an expression is. + +#+BEGIN_HTML +

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

    +#+END_HTML + +#+BEGIN_HTML +
    +#+END_HTML + +Arithmetic + +#+BEGIN_HTML +
    +#+END_HTML + +#+BEGIN_EXAMPLE + 3 + 2 * 6 / 3 ⇔ 3 + ((2*6)/3) +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +Logic + +#+BEGIN_HTML +
    +#+END_HTML + +#+BEGIN_EXAMPLE + True || False ⇒ True + True && False ⇒ False + True == False ⇒ False + True /= False ⇒ True (/=) is the operator for different +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +Powers + +#+BEGIN_HTML +
    +#+END_HTML + +#+BEGIN_EXAMPLE + x^n for n an integral (understand Int or Integer) + x**y for y any kind of number (Float for example) +#+END_EXAMPLE + +=Integer= has no limit except the capacity of your machine: + +#+BEGIN_EXAMPLE + 4^103 + 102844034832575377634685573909834406561420991602098741459288064 +#+END_EXAMPLE + +Yeah! And also rational numbers FTW! But you need to import the module +=Data.Ratio=: + +#+BEGIN_EXAMPLE + $ ghci + .... + Prelude> :m Data.Ratio + Data.Ratio> (11 % 15) * (5 % 3) + 11 % 9 +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +Lists + +#+BEGIN_HTML +
    +#+END_HTML + +#+BEGIN_EXAMPLE + [] ⇔ empty list + [1,2,3] ⇔ List of integral + ["foo","bar","baz"] ⇔ List of String + 1:[2,3] ⇔ [1,2,3], (:) prepend one element + 1:2:[] ⇔ [1,2] + [1,2] ++ [3,4] ⇔ [1,2,3,4], (++) concatenate + [1,2,3] ++ ["foo"] ⇔ ERROR String ≠ Integral + [1..4] ⇔ [1,2,3,4] + [1,3..10] ⇔ [1,3,5,7,9] + [2,3,5,7,11..100] ⇔ ERROR! I am not so smart! + [10,9..1] ⇔ [10,9,8,7,6,5,4,3,2,1] +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +Strings + +#+BEGIN_HTML +
    +#+END_HTML + +In Haskell strings are list of =Char=. + +#+BEGIN_EXAMPLE + 'a' :: Char + "a" :: [Char] + "" ⇔ [] + "ab" ⇔ ['a','b'] ⇔ 'a':"b" ⇔ 'a':['b'] ⇔ 'a':'b':[] + "abc" ⇔ "ab"++"c" +#+END_EXAMPLE + +#+BEGIN_QUOTE + /Remark/: In real code you shouldn't use list of char to represent + text. You should mostly use =Data.Text= instead. If you want to + represent a stream of ASCII char, you should use =Data.ByteString=. +#+END_QUOTE + +#+BEGIN_HTML +
    +#+END_HTML + +Tuples + +#+BEGIN_HTML +
    +#+END_HTML + +The type of couple is =(a,b)=. Elements in a tuple can have different +types. + +#+BEGIN_EXAMPLE + -- All these tuples are valid + (2,"foo") + (3,'a',[2,3]) + ((2,"a"),"c",3) + + fst (x,y) ⇒ x + snd (x,y) ⇒ y + + fst (x,y,z) ⇒ ERROR: fst :: (a,b) -> a + snd (x,y,z) ⇒ ERROR: snd :: (a,b) -> b +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +Deal with parentheses + +#+BEGIN_HTML +
    +#+END_HTML + +To remove some parentheses you can use two functions: =($)= and =(.)=. + +#+BEGIN_EXAMPLE + -- By default: + f g h x ⇔ (((f g) h) x) + + -- the $ replace parenthesis from the $ + -- to the end of the expression + f g $ h x ⇔ f g (h x) ⇔ (f g) (h x) + f $ g h x ⇔ f (g h x) ⇔ f ((g h) x) + f $ g $ h x ⇔ f (g (h x)) + + -- (.) the composition function + (f . g) x ⇔ f (g x) + (f . g . h) x ⇔ f (g (h x)) +#+END_EXAMPLE + +#+BEGIN_HTML +
    +#+END_HTML + +01_basic/20_Essential_Haskell/10a_Functions.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +Just a reminder: + +#+BEGIN_EXAMPLE + x :: Int ⇔ x is of type Int + x :: a ⇔ x can be of any type + x :: Num a => a ⇔ x can be any type a + such that a belongs to Num type class + f :: a -> b ⇔ f is a function from a to b + f :: a -> b -> c ⇔ f is a function from a to (b→c) + f :: (a -> b) -> c ⇔ f is a function from (a→b) to c +#+END_EXAMPLE + +Remember that defining the type of a function before its declaration +isn't mandatory. Haskell infers the most general type for you. But it is +considered a good practice to do so. + +/Infix notation/ + +#+BEGIN_SRC haskell :eval never-export + square :: Num a => a -> a + square x = x^2 +#+END_SRC + +Note =^= uses infix notation. For each infix operator there its +associated prefix notation. You just have to put it inside parenthesis. + +#+BEGIN_SRC haskell :eval never-export + square' x = (^) x 2 + + square'' x = (^2) x +#+END_SRC + +We can remove =x= in the left and right side! It's called η-reduction. + +#+BEGIN_SRC haskell :eval never-export + square''' = (^2) +#+END_SRC + +Note we can declare functions with ='= in their name. Here: + +#+BEGIN_QUOTE + =square= ⇔ =square'= ⇔ =square''= ⇔ =square'''= +#+END_QUOTE + +/Tests/ + +An implementation of the absolute function. + +#+BEGIN_SRC haskell :eval never-export + absolute :: (Ord a, Num a) => a -> a + absolute x = if x >= 0 then x else -x +#+END_SRC + +Note: the =if .. then .. else= Haskell notation is more like the =¤?¤:¤= +C operator. You cannot forget the =else=. + +Another equivalent version: + +#+BEGIN_SRC haskell :eval never-export + absolute' x + | x >= 0 = x + | otherwise = -x +#+END_SRC + +#+BEGIN_QUOTE + Notation warning: indentation is /important/ in Haskell. Like in + Python, bad indentation can break your code! +#+END_QUOTE + +#+BEGIN_SRC haskell :eval never-export + main = do + print $ square 10 + print $ square' 10 + print $ square'' 10 + print $ square''' 10 + print $ absolute 10 + print $ absolute (-10) + print $ absolute' 10 + print $ absolute' (-10) +#+END_SRC + +01_basic/20_Essential_Haskell/10a_Functions.lhs + +#+BEGIN_HTML +

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

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

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

    +#+END_HTML + +blogimage("hr_giger_biomechanicallandscape_500.jpg","Biomechanical +Landscape by H.R. Giger") + +In this section, I will give a short example of the impressive +refactoring ability provided by Haskell. We will select a problem and +solve it in a standard imperative way. Then I will make the code evolve. +The end result will be both more elegant and easier to adapt. + +Let's solve the following problem: + +#+BEGIN_QUOTE + Given a list of integers, return the sum of the even numbers in the + list. + + example: =[1,2,3,4,5] ⇒ 2 + 4 ⇒ 6= +#+END_QUOTE + +To show differences between functional and imperative approaches, I'll +start by providing an imperative solution (in JavaScript): + +#+BEGIN_EXAMPLE + function evenSum(list) { + var result = 0; + for (var i=0; i< list.length ; i++) { + if (list[i] % 2 ==0) { + result += list[i]; + } + } + return result; + } +#+END_EXAMPLE + +In Haskell, by contrast, we don't have variables or a for loop. One +solution to achieve the same result without loops is to use recursion. + +#+BEGIN_QUOTE + /Remark/: Recursion is generally perceived as slow in imperative + languages. But this is generally not the case in functional + programming. Most of the time Haskell will handle recursive functions + efficiently. +#+END_QUOTE + +Here is a =C= version of the recursive function. Note that for +simplicity I assume the int list ends with the first =0= value. + +#+BEGIN_SRC C + int evenSum(int *list) { + return accumSum(0,list); + } + + int accumSum(int n, int *list) { + int x; + int *xs; + if (*list == 0) { // if the list is empty + return n; + } else { + x = list[0]; // let x be the first element of the list + xs = list+1; // let xs be the list without x + if ( 0 == (x%2) ) { // if x is even + return accumSum(n+x, xs); + } else { + return accumSum(n, xs); + } + } + } +#+END_SRC + +Keep this code in mind. We will translate it into Haskell. First, +however, I need to introduce three simple but useful functions we will +use: + +#+BEGIN_SRC haskell :eval never-export + even :: Integral a => a -> Bool + head :: [a] -> a + tail :: [a] -> [a] +#+END_SRC + +=even= verifies if a number is even. + +#+BEGIN_SRC haskell :eval never-export + even :: Integral a => a -> Bool + even 3 ⇒ False + even 2 ⇒ True +#+END_SRC + +=head= returns the first element of a list: + +#+BEGIN_SRC haskell :eval never-export + head :: [a] -> a + head [1,2,3] ⇒ 1 + head [] ⇒ ERROR +#+END_SRC + +=tail= returns all elements of a list, except the first: + +#+BEGIN_SRC haskell :eval never-export + tail :: [a] -> [a] + tail [1,2,3] ⇒ [2,3] + tail [3] ⇒ [] + tail [] ⇒ ERROR +#+END_SRC + +Note that for any non empty list =l=, =l ⇔ (head l):(tail l)= + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/11_Functions.lhs + +The first Haskell solution. The function =evenSum= returns the sum of +all even numbers in a list: + +#+BEGIN_SRC haskell :eval never-export + -- Version 1 + evenSum :: [Integer] -> Integer + + evenSum l = accumSum 0 l + + accumSum n l = if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +#+END_SRC + +To test a function you can use =ghci=: + +#+BEGIN_HTML +
    +  % ghci
    +  GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
    +  Loading package ghc-prim ... linking ... done.
    +  Loading package integer-gmp ... linking ... done.
    +  Loading package base ... linking ... done.
    +  Prelude> :load 11_Functions.lhs
    +  [1 of 1] Compiling Main             ( 11_Functions.lhs, interpreted )
    +  Ok, modules loaded: Main.
    +  *Main> evenSum [1..5]
    +  6
    +  
    +#+END_HTML + +Here is an example of execution[fn:2]: + +#+BEGIN_HTML +
    +  *Main> evenSum [1..5]
    +  accumSum 0 [1,2,3,4,5]
    +  1 is odd
    +  accumSum 0 [2,3,4,5]
    +  2 is even
    +  accumSum (0+2) [3,4,5]
    +  3 is odd
    +  accumSum (0+2) [4,5]
    +  2 is even
    +  accumSum (0+2+4) [5]
    +  5 is odd
    +  accumSum (0+2+4) []
    +  l == []
    +  0+2+4
    +  0+6
    +  6
    +  
    +#+END_HTML + +Coming from an imperative language all should seem right. In fact, many +things can be improved here. First, we can generalize the type. + +#+BEGIN_SRC haskell :eval never-export + evenSum :: Integral a => [a] -> a +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + main = do print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/11_Functions.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/12_Functions.lhs + +Next, we can use sub functions using =where= or =let=. This way our +=accumSum= function won't pollute the namespace of our module. + +#+BEGIN_SRC haskell :eval never-export + -- Version 2 + evenSum :: Integral a => [a] -> a + + evenSum l = accumSum 0 l + where accumSum n l = + if l == [] + then n + else let x = head l + xs = tail l + in if even x + then accumSum (n+x) xs + else accumSum n xs +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + main = print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/12_Functions.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/13_Functions.lhs + +Next, we can use pattern matching. + +#+BEGIN_SRC haskell :eval never-export + -- Version 3 + evenSum l = accumSum 0 l + where + accumSum n [] = n + accumSum n (x:xs) = + if even x + then accumSum (n+x) xs + else accumSum n xs +#+END_SRC + +What is pattern matching? Use values instead of general parameter +names[fn:3]. + +Instead of saying: =foo l = if l == [] then else = You simply +state: + +#+BEGIN_SRC haskell :eval never-export + foo [] = + foo l = +#+END_SRC + +But pattern matching goes even further. It is also able to inspect the +inner data of a complex value. We can replace + +#+BEGIN_SRC haskell :eval never-export + foo l = let x = head l + xs = tail l + in if even x + then foo (n+x) xs + else foo n xs +#+END_SRC + +with + +#+BEGIN_SRC haskell :eval never-export + foo (x:xs) = if even x + then foo (n+x) xs + else foo n xs +#+END_SRC + +This is a very useful feature. It makes our code both terser and easier +to read. + +#+BEGIN_SRC haskell :eval never-export + main = print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/13_Functions.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/14_Functions.lhs + +In Haskell you can simplify function definitions by η-reducing them. For +example, instead of writing: + +#+BEGIN_SRC haskell :eval never-export + f x = (some expresion) x +#+END_SRC + +you can simply write + +#+BEGIN_SRC haskell :eval never-export + f = some expression +#+END_SRC + +We use this method to remove the =l=: + +#+BEGIN_SRC haskell :eval never-export + -- Version 4 + evenSum :: Integral a => [a] -> a + + evenSum = accumSum 0 + where + accumSum n [] = n + accumSum n (x:xs) = + if even x + then accumSum (n+x) xs + else accumSum n xs +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + main = print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/14_Functions.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/15_Functions.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("escher_polygon.png","Escher") + +To make things even better we should use higher order functions. What +are these beasts? Higher order functions are functions taking functions +as parameters. + +Here are some examples: + +#+BEGIN_SRC haskell :eval never-export + filter :: (a -> Bool) -> [a] -> [a] + map :: (a -> b) -> [a] -> [b] + foldl :: (a -> b -> a) -> a -> [b] -> a +#+END_SRC + +Let's proceed by small steps. + +#+BEGIN_SRC haskell :eval never-export + -- Version 5 + evenSum l = mysum 0 (filter even l) + where + mysum n [] = n + mysum n (x:xs) = mysum (n+x) xs +#+END_SRC + +where + +#+BEGIN_SRC haskell :eval never-export + filter even [1..10] ⇔ [2,4,6,8,10] +#+END_SRC + +The function =filter= takes a function of type (=a -> Bool=) and a list +of type =[a]=. It returns a list containing only elements for which the +function returned =true=. + +Our next step is to use another technique to accomplish the same thing +as a loop. We will use the =foldl= function to accumulate a value as we +pass through the list. The function =foldl= captures a general coding +pattern: + +#+BEGIN_HTML +
    +      myfunc list = foo initialValue list
    +      foo accumulated []     = accumulated
    +      foo tmpValue    (x:xs) = foo (bar tmpValue x) xs
    +  
    +#+END_HTML + +Which can be replaced by: + +#+BEGIN_HTML +
    +  myfunc list = foldl bar initialValue list
    +  
    +#+END_HTML + +If you really want to know how the magic works, here is the definition +of =foldl=: + +#+BEGIN_SRC haskell :eval never-export + foldl f z [] = z + foldl f z (x:xs) = foldl f (f z x) xs +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + foldl f z [x1,...xn] + ⇔ f (... (f (f z x1) x2) ...) xn +#+END_SRC + +But as Haskell is lazy, it doesn't evaluate =(f z x)= and simply pushes +it onto the stack. This is why we generally use =foldl'= instead of +=foldl=; =foldl'= is a /strict/ version of =foldl=. If you don't +understand what lazy and strict means, don't worry, just follow the code +as if =foldl= and =foldl'= were identical. + +Now our new version of =evenSum= becomes: + +#+BEGIN_SRC haskell :eval never-export + -- Version 6 + -- foldl' isn't accessible by default + -- we need to import it from the module Data.List + import Data.List + evenSum l = foldl' mysum 0 (filter even l) + where mysum acc value = acc + value +#+END_SRC + +We can also simplify this by using directly a lambda notation. This way +we don't have to create the temporary name =mysum=. + +#+BEGIN_SRC haskell :eval never-export + -- Version 7 + -- Generally it is considered a good practice + -- to import only the necessary function(s) + import Data.List (foldl') + evenSum l = foldl' (\x y -> x+y) 0 (filter even l) +#+END_SRC + +And of course, we note that + +#+BEGIN_SRC haskell :eval never-export + (\x y -> x+y) ⇔ (+) +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + main = print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/15_Functions.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/16_Functions.lhs + +Finally + +#+BEGIN_SRC haskell :eval never-export + -- Version 8 + import Data.List (foldl') + evenSum :: Integral a => [a] -> a + evenSum l = foldl' (+) 0 (filter even l) +#+END_SRC + +=foldl'= isn't the easiest function to grasp. If you are not used to it, +you should study it a bit. + +To help you understand what's going on here, let's look at a step by +step evaluation: + +#+BEGIN_HTML +
    +    evenSum [1,2,3,4]
    +  ⇒ foldl' (+) 0 (filter even [1,2,3,4])
    +  ⇒ foldl' (+) 0 [2,4]
    +  ⇒ foldl' (+) (0+2) [4]
    +  ⇒ foldl' (+) 2 [4]
    +  ⇒ foldl' (+) (2+4) []
    +  ⇒ foldl' (+) 6 []
    +  ⇒ 6
    +  
    +#+END_HTML + +Another useful higher order function is =(.)=. The =(.)= function +corresponds to mathematical composition. + +#+BEGIN_SRC haskell :eval never-export + (f . g . h) x ⇔ f ( g (h x)) +#+END_SRC + +We can take advantage of this operator to η-reduce our function: + +#+BEGIN_SRC haskell :eval never-export + -- Version 9 + import Data.List (foldl') + evenSum :: Integral a => [a] -> a + evenSum = (foldl' (+) 0) . (filter even) +#+END_SRC + +Also, we could rename some parts to make it clearer: + +#+BEGIN_SRC haskell :eval never-export + -- Version 10 + import Data.List (foldl') + sum' :: (Num a) => [a] -> a + sum' = foldl' (+) 0 + evenSum :: Integral a => [a] -> a + evenSum = sum' . (filter even) +#+END_SRC + +It is time to discuss the direction our code has moved as we introduced +more functional idioms. What did we gain by using higher order +functions? + +At first, you might think the main difference is terseness. But in fact, +it has more to do with better thinking. Suppose we want to modify our +function slightly, for example, to get the sum of all even squares of +elements of the list. + +#+BEGIN_EXAMPLE + [1,2,3,4] ▷ [1,4,9,16] ▷ [4,16] ▷ 20 +#+END_EXAMPLE + +Updating version 10 is extremely easy: + +#+BEGIN_SRC haskell :eval never-export + squareEvenSum = sum' . (filter even) . (map (^2)) + squareEvenSum' = evenSum . (map (^2)) +#+END_SRC + +We just had to add another "transformation function"[^0216]. + +#+BEGIN_EXAMPLE + map (^2) [1,2,3,4] ⇔ [1,4,9,16] +#+END_EXAMPLE + +The =map= function simply applies a function to all the elements of a +list. + +We didn't have to modify anything /inside/ the function definition. This +makes the code more modular. But in addition you can think more +mathematically about your function. You can also use your function +interchangably with others, as needed. That is, you can compose, map, +fold, filter using your new function. + +Modifying version 1 is left as an exercise to the reader ☺. + +If you believe we have reached the end of generalization, then know you +are very wrong. For example, there is a way to not only use this +function on lists but on any recursive type. If you want to know how, I +suggest you to read this quite fun article: +[[http://eprints.eemcs.utwente.nl/7281/01/db-utwente-40501F46.pdf][Functional +Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, +Fokkinga and Paterson]]. + +This example should show you how great pure functional programming is. +Unfortunately, using pure functional programming isn't well suited to +all usages. Or at least such a language hasn't been found yet. + +One of the great powers of Haskell is the ability to create DSLs (Domain +Specific Language) making it easy to change the programming paradigm. + +In fact, Haskell is also great when you want to write imperative style +programming. Understanding this was really hard for me to grasp when +first learning Haskell. A lot of effort tends to go into explaining the +superiority of the functional approach. Then when you start using an +imperative style with Haskell, it can be hard to understand when and how +to use it. + +But before talking about this Haskell super-power, we must talk about +another essential aspect of Haskell: /Types/. + +#+BEGIN_SRC haskell :eval never-export + main = print $ evenSum [1..10] +#+END_SRC + +02_Hard_Part/16_Functions.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("salvador-dali-the-madonna-of-port-lligat.jpg","Dali, the +madonna of port Lligat") + +#+BEGIN_QUOTE + %tldr + + - =type Name = AnotherType= is just an alias and the compiler doesn't + mark any difference between =Name= and =AnotherType=. + - =data Name = NameConstructor AnotherType= does mark a difference. + - =data= can construct structures which can be recursives. + - =deriving= is magic and creates functions for you. +#+END_QUOTE + +In Haskell, types are strong and static. + +Why is this important? It will help you /greatly/ to avoid mistakes. In +Haskell, most bugs are caught during the compilation of your program. +And the main reason is because of the type inference during compilation. +Type inference makes it easy to detect where you used the wrong +parameter at the wrong place, for example. + +#+BEGIN_HTML +

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

    +#+END_HTML + +Static typing is generally essential for fast execution. But most +statically typed languages are bad at generalizing concepts. Haskell's +saving grace is that it can /infer/ types. + +Here is a simple example, the =square= function in Haskell: + +#+BEGIN_SRC haskell :eval never-export + square x = x * x +#+END_SRC + +This function can =square= any Numeral type. You can provide =square= +with an =Int=, an =Integer=, a =Float= a =Fractional= and even +=Complex=. Proof by example: + +#+BEGIN_EXAMPLE + % ghci + GHCi, version 7.0.4: + ... + Prelude> let square x = x*x + Prelude> square 2 + 4 + Prelude> square 2.1 + 4.41 + Prelude> -- load the Data.Complex module + Prelude> :m Data.Complex + Prelude Data.Complex> square (2 :+ 1) + 3.0 :+ 4.0 +#+END_EXAMPLE + +=x :+ y= is the notation for the complex (x + iy). + +Now compare with the amount of code necessary in C: + +#+BEGIN_SRC C + int int_square(int x) { return x*x; } + + float float_square(float x) {return x*x; } + + complex complex_square (complex z) { + complex tmp; + tmp.real = z.real * z.real - z.img * z.img; + tmp.img = 2 * z.img * z.real; + } + + complex x,y; + y = complex_square(x); +#+END_SRC + +For each type, you need to write a new function. The only way to work +around this problem is to use some meta-programming trick, for example +using the pre-processor. In C++ there is a better way, C++ templates: + +#+BEGIN_EXAMPLE + #include + #include + using namespace std; + + template + T square(T x) + { + return x*x; + } + + int main() { + // int + int sqr_of_five = square(5); + cout << sqr_of_five << endl; + // double + cout << (double)square(5.3) << endl; + // complex + cout << square( complex(5,3) ) + << endl; + return 0; + } +#+END_EXAMPLE + +C++ does a far better job than C in this regard. But for more complex +functions the syntax can be hard to follow: see +[[http://bartoszmilewski.com/2009/10/21/what-does-haskell-have-to-do-with-c/][this +article]] for example. + +In C++ you must declare that a function can work with different types. +In Haskell, the opposite is the case. The function will be as general as +possible by default. + +Type inference gives Haskell the feeling of freedom that dynamically +typed languages provide. But unlike dynamically typed languages, most +errors are caught before run time. Generally, in Haskell: + +#+BEGIN_QUOTE + "if it compiles it certainly does what you intended" +#+END_QUOTE + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/21_Types.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +You can construct your own types. First, you can use aliases or type +synonyms. + +#+BEGIN_SRC haskell :eval never-export + type Name = String + type Color = String + + showInfos :: Name -> Color -> String + showInfos name color = "Name: " ++ name + ++ ", Color: " ++ color + name :: Name + name = "Robin" + color :: Color + color = "Blue" + main = putStrLn $ showInfos name color +#+END_SRC + +02_Hard_Part/21_Types.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/22_Types.lhs + +But it doesn't protect you much. Try to swap the two parameter of +=showInfos= and run the program: + +#+BEGIN_SRC haskell :eval never-export + putStrLn $ showInfos color name +#+END_SRC + +It will compile and execute. In fact you can replace Name, Color and +String everywhere. The compiler will treat them as completely identical. + +Another method is to create your own types using the keyword =data=. + +#+BEGIN_SRC haskell :eval never-export + data Name = NameConstr String + data Color = ColorConstr String + + showInfos :: Name -> Color -> String + showInfos (NameConstr name) (ColorConstr color) = + "Name: " ++ name ++ ", Color: " ++ color + + name = NameConstr "Robin" + color = ColorConstr "Blue" + main = putStrLn $ showInfos name color +#+END_SRC + +Now if you switch parameters of =showInfos=, the compiler complains! So +this is a potential mistake you will never make again and the only price +is to be more verbose. + +Also notice that constructors are functions: + +#+BEGIN_SRC haskell :eval never-export + NameConstr :: String -> Name + ColorConstr :: String -> Color +#+END_SRC + +The syntax of =data= is mainly: + +#+BEGIN_SRC haskell :eval never-export + data TypeName = ConstructorName [types] + | ConstructorName2 [types] + | ... +#+END_SRC + +Generally the usage is to use the same name for the DataTypeName and +DataTypeConstructor. + +Example: + +#+BEGIN_SRC haskell :eval never-export + data Complex a = Num a => Complex a a +#+END_SRC + +Also you can use the record syntax: + +#+BEGIN_SRC haskell :eval never-export + data DataTypeName = DataConstructor { + field1 :: [type of field1] + , field2 :: [type of field2] + ... + , fieldn :: [type of fieldn] } +#+END_SRC + +And many accessors are made for you. Furthermore you can use another +order when setting values. + +Example: + +#+BEGIN_SRC haskell :eval never-export + data Complex a = Num a => Complex { real :: a, img :: a} + c = Complex 1.0 2.0 + z = Complex { real = 3, img = 4 } + real c ⇒ 1.0 + img z ⇒ 4 +#+END_SRC + +02_Hard_Part/22_Types.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/23_Types.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +You already encountered a recursive type: lists. You can re-create +lists, but with a more verbose syntax: + +#+BEGIN_SRC haskell :eval never-export + data List a = Empty | Cons a (List a) +#+END_SRC + +If you really want to use an easier syntax you can use an infix name for +constructors. + +#+BEGIN_SRC haskell :eval never-export + infixr 5 ::: + data List a = Nil | a ::: (List a) +#+END_SRC + +The number after =infixr= gives the precedence. + +If you want to be able to print (=Show=), read (=Read=), test equality +(=Eq=) and compare (=Ord=) your new data structure you can tell Haskell +to derive the appropriate functions for you. + +#+BEGIN_SRC haskell :eval never-export + infixr 5 ::: + data List a = Nil | a ::: (List a) + deriving (Show,Read,Eq,Ord) +#+END_SRC + +When you add =deriving (Show)= to your data declaration, Haskell creates +a =show= function for you. We'll see soon how you can use your own +=show= function. + +#+BEGIN_SRC haskell :eval never-export + convertList [] = Nil + convertList (x:xs) = x ::: convertList xs +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + main = do + print (0 ::: 1 ::: Nil) + print (convertList [0,1]) +#+END_SRC + +This prints: + +#+BEGIN_EXAMPLE + 0 ::: (1 ::: Nil) + 0 ::: (1 ::: Nil) +#+END_EXAMPLE + +02_Hard_Part/23_Types.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/30_Trees.lhs + +*** Trees + :PROPERTIES: + :CUSTOM_ID: trees + :END: + +blogimage("magritte-l-arbre.jpg","Magritte, l'Arbre") + +We'll just give another standard example: binary trees. + +#+BEGIN_SRC haskell :eval never-export + import Data.List + + data BinTree a = Empty + | Node a (BinTree a) (BinTree a) + deriving (Show) +#+END_SRC + +We will also create a function which turns a list into an ordered binary +tree. + +#+BEGIN_SRC haskell :eval never-export + treeFromList :: (Ord a) => [a] -> BinTree a + treeFromList [] = Empty + treeFromList (x:xs) = Node x (treeFromList (filter (x) xs)) +#+END_SRC + +Look at how elegant this function is. In plain English: + +- an empty list will be converted to an empty tree. +- a list =(x:xs)= will be converted to a tree where: + + - The root is =x= + - Its left subtree is the tree created from members of the list =xs= + which are strictly inferior to =x= and + - the right subtree is the tree created from members of the list =xs= + which are strictly superior to =x=. + +#+BEGIN_SRC haskell :eval never-export + main = print $ treeFromList [7,2,4,8] +#+END_SRC + +You should obtain the following: + +#+BEGIN_EXAMPLE + Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty) +#+END_EXAMPLE + +This is an informative but quite unpleasant representation of our tree. + +02_Hard_Part/30_Trees.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/31_Trees.lhs + +Just for fun, let's code a better display for our trees. I simply had +fun making a nice function to display trees in a general way. You can +safely skip this part if you find it too difficult to follow. + +We have a few changes to make. We remove the =deriving (Show)= from the +declaration of our =BinTree= type. And it might also be useful to make +our BinTree an instance of (=Eq= and =Ord=) so we will be able to test +equality and compare trees. + +#+BEGIN_SRC haskell :eval never-export + data BinTree a = Empty + | Node a (BinTree a) (BinTree a) + deriving (Eq,Ord) +#+END_SRC + +Without the =deriving (Show)=, Haskell doesn't create a =show= method +for us. We will create our own version of =show=. To achieve this, we +must declare that our newly created type =BinTree a= is an instance of +the type class =Show=. The general syntax is: + +#+BEGIN_SRC haskell :eval never-export + instance Show (BinTree a) where + show t = ... -- You declare your function here +#+END_SRC + +Here is my version of how to show a binary tree. Don't worry about the +apparent complexity. I made a lot of improvements in order to display +even stranger objects. + +#+BEGIN_SRC haskell :eval never-export + -- declare BinTree a to be an instance of Show + instance (Show a) => Show (BinTree a) where + -- will start by a '<' before the root + -- and put a : a begining of line + show t = "< " ++ replace '\n' "\n: " (treeshow "" t) + where + -- treeshow pref Tree + -- shows a tree and starts each line with pref + -- We don't display the Empty tree + treeshow pref Empty = "" + -- Leaf + treeshow pref (Node x Empty Empty) = + (pshow pref x) + + -- Right branch is empty + treeshow pref (Node x left Empty) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " left) + + -- Left branch is empty + treeshow pref (Node x Empty right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " right) + + -- Tree with left and right children non empty + treeshow pref (Node x left right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "|--" "| " left) ++ "\n" ++ + (showSon pref "`--" " " right) + + -- shows a tree using some prefixes to make it nice + showSon pref before next t = + pref ++ before ++ treeshow (pref ++ next) t + + -- pshow replaces "\n" by "\n"++pref + pshow pref x = replace '\n' ("\n"++pref) (show x) + + -- replaces one char by another string + replace c new string = + concatMap (change c new) string + where + change c new x + | x == c = new + | otherwise = x:[] -- "x" +#+END_SRC + +The =treeFromList= method remains identical. + +#+BEGIN_SRC haskell :eval never-export + treeFromList :: (Ord a) => [a] -> BinTree a + treeFromList [] = Empty + treeFromList (x:xs) = Node x (treeFromList (filter (x) xs)) +#+END_SRC + +And now, we can play: + +#+BEGIN_SRC haskell :eval never-export + main = do + putStrLn "Int binary tree:" + print $ treeFromList [7,2,4,8,1,3,6,21,12,23] +#+END_SRC + +#+BEGIN_EXAMPLE + Int binary tree: + < 7 + : |--2 + : | |--1 + : | `--4 + : | |--3 + : | `--6 + : `--8 + : `--21 + : |--12 + : `--23 +#+END_EXAMPLE + +Now it is far better! The root is shown by starting the line with the +=<= character. And each following line starts with a =:=. But we could +also use another type. + +#+BEGIN_SRC haskell :eval never-export + putStrLn "\nString binary tree:" + print $ treeFromList ["foo","bar","baz","gor","yog"] +#+END_SRC + +#+BEGIN_EXAMPLE + String binary tree: + < "foo" + : |--"bar" + : | `--"baz" + : `--"gor" + : `--"yog" +#+END_EXAMPLE + +As we can test equality and order trees, we can make tree of trees! + +#+BEGIN_SRC haskell :eval never-export + putStrLn "\nBinary tree of Char binary trees:" + print ( treeFromList + (map treeFromList ["baz","zara","bar"])) +#+END_SRC + +#+BEGIN_EXAMPLE + Binary tree of Char binary trees: + < < 'b' + : : |--'a' + : : `--'z' + : |--< 'b' + : | : |--'a' + : | : `--'r' + : `--< 'z' + : : `--'a' + : : `--'r' +#+END_EXAMPLE + +This is why I chose to prefix each line of tree display by =:= (except +for the root). + +blogimage("yo_dawg_tree.jpg","Yo Dawg Tree") + +#+BEGIN_SRC haskell :eval never-export + putStrLn "\nTree of Binary trees of Char binary trees:" + print $ (treeFromList . map (treeFromList . map treeFromList)) + [ ["YO","DAWG"] + , ["I","HEARD"] + , ["I","HEARD"] + , ["YOU","LIKE","TREES"] ] +#+END_SRC + +Which is equivalent to + +#+BEGIN_SRC haskell :eval never-export + print ( treeFromList ( + map treeFromList + [ map treeFromList ["YO","DAWG"] + , map treeFromList ["I","HEARD"] + , map treeFromList ["I","HEARD"] + , map treeFromList ["YOU","LIKE","TREES"] ])) +#+END_SRC + +and gives: + +#+BEGIN_EXAMPLE + Binary tree of Binary trees of Char binary trees: + < < < 'Y' + : : : `--'O' + : : `--< 'D' + : : : |--'A' + : : : `--'W' + : : : `--'G' + : |--< < 'I' + : | : `--< 'H' + : | : : |--'E' + : | : : | `--'A' + : | : : | `--'D' + : | : : `--'R' + : `--< < 'Y' + : : : `--'O' + : : : `--'U' + : : `--< 'L' + : : : `--'I' + : : : |--'E' + : : : `--'K' + : : `--< 'T' + : : : `--'R' + : : : |--'E' + : : : `--'S' +#+END_EXAMPLE + +Notice how duplicate trees aren't inserted; there is only one tree +corresponding to ="I","HEARD"=. We have this for (almost) free, because +we have declared Tree to be an instance of =Eq=. + +See how awesome this structure is: We can make trees containing not only +integers, strings and chars, but also other trees. And we can even make +a tree containing a tree of trees! + +02_Hard_Part/31_Trees.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/40_Infinites_Structures.lhs + +** Infinite Structures + :PROPERTIES: + :CUSTOM_ID: infinite-structures + :END: + +blogimage("escher_infinite_lizards.jpg","Escher") + +It is often said that Haskell is /lazy/. + +In fact, if you are a bit pedantic, you should say that +[[http://www.haskell.org/haskellwiki/Lazy_vs._non-strict][Haskell is +/non-strict/]]. Laziness is just a common implementation for non-strict +languages. + +Then what does "not-strict" mean? From the Haskell wiki: + +#+BEGIN_QUOTE + Reduction (the mathematical term for evaluation) proceeds from the + outside in. + + so if you have =(a+(b*c))= then you first reduce =+= first, then you + reduce the inner =(b*c)= +#+END_QUOTE + +For example in Haskell you can do: + +#+BEGIN_SRC haskell :eval never-export + -- numbers = [1,2,..] + numbers :: [Integer] + numbers = 0:map (1+) numbers + + take' n [] = [] + take' 0 l = [] + take' n (x:xs) = x:take' (n-1) xs + + main = print $ take' 10 numbers +#+END_SRC + +And it stops. + +How? + +Instead of trying to evaluate =numbers= entirely, it evaluates elements +only when needed. + +Also, note in Haskell there is a notation for infinite lists + +#+BEGIN_EXAMPLE + [1..] ⇔ [1,2,3,4...] + [1,3..] ⇔ [1,3,5,7,9,11...] +#+END_EXAMPLE + +and most functions will work with them. Also, there is a built-in +function =take= which is equivalent to our =take'=. + +02_Hard_Part/40_Infinites_Structures.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +02_Hard_Part/41_Infinites_Structures.lhs + +This code is mostly the same as the previous one. + +#+BEGIN_SRC haskell :eval never-export + import Debug.Trace (trace) + import Data.List + data BinTree a = Empty + | Node a (BinTree a) (BinTree a) + deriving (Eq,Ord) +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + -- declare BinTree a to be an instance of Show + instance (Show a) => Show (BinTree a) where + -- will start by a '<' before the root + -- and put a : a begining of line + show t = "< " ++ replace '\n' "\n: " (treeshow "" t) + where + treeshow pref Empty = "" + treeshow pref (Node x Empty Empty) = + (pshow pref x) + + treeshow pref (Node x left Empty) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " left) + + treeshow pref (Node x Empty right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " right) + + treeshow pref (Node x left right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "|--" "| " left) ++ "\n" ++ + (showSon pref "`--" " " right) + + -- show a tree using some prefixes to make it nice + showSon pref before next t = + pref ++ before ++ treeshow (pref ++ next) t + + -- pshow replace "\n" by "\n"++pref + pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x) + + -- replace on char by another string + replace c new string = + concatMap (change c new) string + where + change c new x + | x == c = new + | otherwise = x:[] -- "x" +#+END_SRC + +Suppose we don't mind having an ordered binary tree. Here is an infinite +binary tree: + +#+BEGIN_SRC haskell :eval never-export + nullTree = Node 0 nullTree nullTree +#+END_SRC + +A complete binary tree where each node is equal to 0. Now I will prove +you can manipulate this object using the following function: + +#+BEGIN_SRC haskell :eval never-export + -- take all element of a BinTree + -- up to some depth + treeTakeDepth _ Empty = Empty + treeTakeDepth 0 _ = Empty + treeTakeDepth n (Node x left right) = let + nl = treeTakeDepth (n-1) left + nr = treeTakeDepth (n-1) right + in + Node x nl nr +#+END_SRC + +See what occurs for this program: + +#+BEGIN_SRC haskell :eval never-export + main = print $ treeTakeDepth 4 nullTree +#+END_SRC + +This code compiles, runs and stops giving the following result: + +#+BEGIN_EXAMPLE + < 0 + : |-- 0 + : | |-- 0 + : | | |-- 0 + : | | `-- 0 + : | `-- 0 + : | |-- 0 + : | `-- 0 + : `-- 0 + : |-- 0 + : | |-- 0 + : | `-- 0 + : `-- 0 + : |-- 0 + : `-- 0 +#+END_EXAMPLE + +Just to heat up your neurones a bit more, let's make a slightly more +interesting tree: + +#+BEGIN_SRC haskell :eval never-export + iTree = Node 0 (dec iTree) (inc iTree) + where + dec (Node x l r) = Node (x-1) (dec l) (dec r) + inc (Node x l r) = Node (x+1) (inc l) (inc r) +#+END_SRC + +Another way to create this tree is to use a higher order function. This +function should be similar to =map=, but should work on =BinTree= +instead of list. Here is such a function: + +#+BEGIN_SRC haskell :eval never-export + -- apply a function to each node of Tree + treeMap :: (a -> b) -> BinTree a -> BinTree b + treeMap f Empty = Empty + treeMap f (Node x left right) = Node (f x) + (treeMap f left) + (treeMap f right) +#+END_SRC + +/Hint/: I won't talk more about this here. If you are interested in the +generalization of =map= to other data structures, search for functor and +=fmap=. + +Our definition is now: + +#+BEGIN_SRC haskell :eval never-export + infTreeTwo :: BinTree Int + infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo) + (treeMap (\x -> x+1) infTreeTwo) +#+END_SRC + +Look at the result for + +#+BEGIN_SRC haskell :eval never-export + main = print $ treeTakeDepth 4 infTreeTwo +#+END_SRC + +#+BEGIN_EXAMPLE + < 0 + : |-- -1 + : | |-- -2 + : | | |-- -3 + : | | `-- -1 + : | `-- 0 + : | |-- -1 + : | `-- 1 + : `-- 1 + : |-- 0 + : | |-- -1 + : | `-- 1 + : `-- 2 + : |-- 1 + : `-- 3 +#+END_EXAMPLE + +#+BEGIN_SRC haskell :eval never-export + main = do + print $ treeTakeDepth 4 nullTree + print $ treeTakeDepth 4 infTreeTwo +#+END_SRC + +02_Hard_Part/41_Infinites_Structures.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +Congratulations for getting so far! Now, some of the really hardcore +stuff can start. + +If you are like me, you should get the functional style. You should also +understand a bit more the advantages of laziness by default. But you +also don't really understand where to start in order to make a real +program. And in particular: + +- How do you deal with effects? +- Why is there a strange imperative-like notation for dealing with IO? + +Be prepared, the answers might be complex. But they are all very +rewarding. + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/01_IO/01_progressive_io_example.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("magritte_carte_blanche.jpg","Magritte, Carte blanche") + +#+BEGIN_QUOTE + %tldr + + A typical function doing =IO= looks a lot like an imperative program: + + #+BEGIN_EXAMPLE + f :: IO a + f = do + x <- action1 + action2 x + y <- action3 + action4 x y + #+END_EXAMPLE + + - To set a value to an object we use =<-= . + - The type of each line is =IO *=; in this example: + + - =action1 :: IO b= + - =action2 x :: IO ()= + - =action3 :: IO c= + - =action4 x y :: IO a= + - =x :: b=, =y :: c= + + - Few objects have the type =IO a=, this should help you choose. In + particular you cannot use pure functions directly here. To use pure + functions you could do =action2 (purefunction x)= for example. +#+END_QUOTE + +In this section, I will explain how to use IO, not how it works. You'll +see how Haskell separates the pure from the impure parts of the program. + +Don't stop because you're trying to understand the details of the +syntax. Answers will come in the next section. + +What to achieve? + +#+BEGIN_QUOTE + Ask a user to enter a list of numbers. Print the sum of the numbers +#+END_QUOTE + +#+BEGIN_SRC haskell :eval never-export + toList :: String -> [Integer] + toList input = read ("[" ++ input ++ "]") + + main = do + putStrLn "Enter a list of numbers (separated by comma):" + input <- getLine + print $ sum (toList input) +#+END_SRC + +It should be straightforward to understand the behavior of this program. +Let's analyze the types in more detail. + +#+BEGIN_EXAMPLE + putStrLn :: String -> IO () + getLine :: IO String + print :: Show a => a -> IO () +#+END_EXAMPLE + +Or more interestingly, we note that each expression in the =do= block +has a type of =IO a=. + +#+BEGIN_HTML +
    +  main = do
    +    putStrLn "Enter ... " :: IO ()
    +    getLine               :: IO String
    +    print Something       :: IO ()
    +  
    +#+END_HTML + +We should also pay attention to the effect of the =<-= symbol. + +#+BEGIN_EXAMPLE + do + x <- something +#+END_EXAMPLE + +If =something :: IO a= then =x :: a=. + +Another important note about using =IO=: All lines in a do block must be +of one of the two forms: + +#+BEGIN_EXAMPLE + action1 :: IO a + -- in this case, generally a = () +#+END_EXAMPLE + +ou + +#+BEGIN_EXAMPLE + value <- action2 -- where + -- action2 :: IO b + -- value :: b +#+END_EXAMPLE + +These two kinds of line will correspond to two different ways of +sequencing actions. The meaning of this sentence should be clearer by +the end of the next section. + +03_Hell/01_IO/01_progressive_io_example.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/01_IO/02_progressive_io_example.lhs + +Now let's see how this program behaves. For example, what happens if the +user enters something strange? Let's try: + +#+BEGIN_EXAMPLE + % runghc 02_progressive_io_example.lhs + Enter a list of numbers (separated by comma): + foo + Prelude.read: no parse +#+END_EXAMPLE + +Argh! An evil error message and a crash! Our first improvement will +simply be to answer with a more friendly message. + +In order to do this, we must detect that something went wrong. Here is +one way to do this: use the type =Maybe=. This is a very common type in +Haskell. + +#+BEGIN_SRC haskell :eval never-export + import Data.Maybe +#+END_SRC + +What is this thing? =Maybe= is a type which takes one parameter. Its +definition is: + +#+BEGIN_SRC haskell :eval never-export + data Maybe a = Nothing | Just a +#+END_SRC + +This is a nice way to tell there was an error while trying to +create/compute a value. The =maybeRead= function is a great example of +this. This is a function similar to the function =read=[fn:4], but if +something goes wrong the returned value is =Nothing=. If the value is +right, it returns =Just =. Don't try to understand too much +of this function. I use a lower level function than =read=: =reads=. + +#+BEGIN_SRC haskell :eval never-export + maybeRead :: Read a => String -> Maybe a + maybeRead s = case reads s of + [(x,"")] -> Just x + _ -> Nothing +#+END_SRC + +Now to be a bit more readable, we define a function which goes like +this: If the string has the wrong format, it will return =Nothing=. +Otherwise, for example for "1,2,3", it will return =Just [1,2,3]=. + +#+BEGIN_SRC haskell :eval never-export + getListFromString :: String -> Maybe [Integer] + getListFromString str = maybeRead $ "[" ++ str ++ "]" +#+END_SRC + +We simply have to test the value in our main function. + +#+BEGIN_SRC haskell :eval never-export + main :: IO () + main = do + putStrLn "Enter a list of numbers (separated by comma):" + input <- getLine + let maybeList = getListFromString input in + case maybeList of + Just l -> print (sum l) + Nothing -> error "Bad format. Good Bye." +#+END_SRC + +In case of error, we display a nice error message. + +Note that the type of each expression in the main's =do= block remains +of the form =IO a=. The only strange construction is =error=. I'll just +say here that =error msg= takes the needed type (here =IO ()=). + +One very important thing to note is the type of all the functions +defined so far. There is only one function which contains =IO= in its +type: =main=. This means main is impure. But main uses +=getListFromString= which is pure. So it's clear just by looking at +declared types which functions are pure and which are impure. + +Why does purity matter? Among the many advantages, here are three: + +- It is far easier to think about pure code than impure code. +- Purity protects you from all the hard-to-reproduce bugs that are due + to side effects. +- You can evaluate pure functions in any order or in parallel without + risk. + +This is why you should generally put as most code as possible inside +pure functions. + +03_Hell/01_IO/02_progressive_io_example.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/01_IO/03_progressive_io_example.lhs + +Our next iteration will be to prompt the user again and again until she +enters a valid answer. + +We keep the first part: + +#+BEGIN_SRC haskell :eval never-export + import Data.Maybe + + maybeRead :: Read a => String -> Maybe a + maybeRead s = case reads s of + [(x,"")] -> Just x + _ -> Nothing + getListFromString :: String -> Maybe [Integer] + getListFromString str = maybeRead $ "[" ++ str ++ "]" +#+END_SRC + +Now we create a function which will ask the user for an list of integers +until the input is right. + +#+BEGIN_SRC haskell :eval never-export + askUser :: IO [Integer] + askUser = do + putStrLn "Enter a list of numbers (separated by comma):" + input <- getLine + let maybeList = getListFromString input in + case maybeList of + Just l -> return l + Nothing -> askUser +#+END_SRC + +This function is of type =IO [Integer]=. Such a type means that we +retrieved a value of type =[Integer]= through some IO actions. Some +people might explain while waving their hands: + +#+BEGIN_QUOTE + «This is an =[Integer]= inside an =IO=» +#+END_QUOTE + +If you want to understand the details behind all of this, you'll have to +read the next section. But really, if you just want to /use/ IO just +practice a little and remember to think about the type. + +Finally our main function is much simpler: + +#+BEGIN_SRC haskell :eval never-export + main :: IO () + main = do + list <- askUser + print $ sum list +#+END_SRC + +We have finished with our introduction to =IO=. This was quite fast. +Here are the main things to remember: + +- in the =do= block, each expression must have the type =IO a=. You are + then limited with regard to the range of expressions available. For + example, =getLine=, =print=, =putStrLn=, etc... +- Try to externalize the pure functions as much as possible. +- the =IO a= type means: an IO /action/ which returns an element of type + =a=. =IO= represents actions; under the hood, =IO a= is the type of a + function. Read the next section if you are curious. + +If you practice a bit, you should be able to /use/ =IO=. + +#+BEGIN_QUOTE + /Exercises/: + + - Make a program that sums all of its arguments. Hint: use the + function =getArgs=. +#+END_QUOTE + +03_Hell/01_IO/03_progressive_io_example.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("magritte_pipe.jpg","Magritte, ceci n'est pas une pipe") + +#+BEGIN_QUOTE + Here is a %tldr for this section. + + To separate pure and impure parts, =main= is defined as a function + which modifies the state of the world. + + #+BEGIN_EXAMPLE + main :: World -> World + #+END_EXAMPLE + + A function is guaranteed to have side effects only if it has this + type. But look at a typical main function: + + #+BEGIN_EXAMPLE + + main w0 = + let (v1,w1) = action1 w0 in + let (v2,w2) = action2 v1 w1 in + let (v3,w3) = action3 v2 w2 in + action4 v3 w3 + #+END_EXAMPLE + + We have a lot of temporary elements (here =w1=, =w2= and =w3=) which + must be passed on to the next action. + + We create a function =bind= or =(>>=)=. With =bind= we don't need + temporary names anymore. + + #+BEGIN_EXAMPLE + main = + action1 >>= action2 >>= action3 >>= action4 + #+END_EXAMPLE + + Bonus: Haskell has syntactical sugar for us: + + #+BEGIN_EXAMPLE + main = do + v1 <- action1 + v2 <- action2 v1 + v3 <- action3 v2 + action4 v3 + #+END_EXAMPLE +#+END_QUOTE + +Why did we use this strange syntax, and what exactly is this =IO= type? +It looks a bit like magic. + +For now let's just forget all about the pure parts of our program, and +focus on the impure parts: + +#+BEGIN_SRC haskell :eval never-export + askUser :: IO [Integer] + askUser = do + putStrLn "Enter a list of numbers (separated by commas):" + input <- getLine + let maybeList = getListFromString input in + case maybeList of + Just l -> return l + Nothing -> askUser + + main :: IO () + main = do + list <- askUser + print $ sum list +#+END_SRC + +First remark: this looks imperative. Haskell is powerful enough to make +impure code look imperative. For example, if you wish you could create a +=while= in Haskell. In fact, for dealing with =IO=, an imperative style +is generally more appropriate. + +But you should have noticed that the notation is a bit unusual. Here is +why, in detail. + +In an impure language, the state of the world can be seen as a huge +hidden global variable. This hidden variable is accessible by all +functions of your language. For example, you can read and write a file +in any function. Whether a file exists or not is a difference in the +possible states that the world can take. + +In Haskell the current state of the world is not hidden. Rather, it is +/explicitly/ said that =main= is a function that /potentially/ changes +the state of the world. Its type is then something like: + +#+BEGIN_SRC haskell :eval never-export + main :: World -> World +#+END_SRC + +Not all functions may access this variable. Those which have access to +this variable are impure. Functions to which the world variable isn't +provided are pure[fn:5]. + +Haskell considers the state of the world as an input variable to =main=. +But the real type of main is closer to this one[fn:6]: + +#+BEGIN_SRC haskell :eval never-export + main :: World -> ((),World) +#+END_SRC + +The =()= type is the unit type. Nothing to see here. + +Now let's rewrite our main function with this in mind: + +#+BEGIN_SRC haskell :eval never-export + main w0 = + let (list,w1) = askUser w0 in + let (x,w2) = print (sum list,w1) in + x +#+END_SRC + +First, we note that all functions which have side effects must have the +type: + +#+BEGIN_SRC haskell :eval never-export + World -> (a,World) +#+END_SRC + +where =a= is the type of the result. For example, a =getChar= function +should have the type =World -> (Char, World)=. + +Another thing to note is the trick to fix the order of evaluation. In +Haskell, in order to evaluate =f a b=, you have many choices: + +- first eval =a= then =b= then =f a b= +- first eval =b= then =a= then =f a b=. +- eval =a= and =b= in parallel then =f a b= + +This is true because we're working in a pure part of the language. + +Now, if you look at the main function, it is clear you must eval the +first line before the second one since to evaluate the second line you +have to get a parameter given by the evaluation of the first line. + +This trick works like a charm. The compiler will at each step provide a +pointer to a new real world id. Under the hood, =print= will evaluate +as: + +- print something on the screen +- modify the id of the world +- evaluate as =((),new world id)=. + +Now, if you look at the style of the main function, it is clearly +awkward. Let's try to do the same to the =askUser= function: + +#+BEGIN_SRC haskell :eval never-export + askUser :: World -> ([Integer],World) +#+END_SRC + +Before: + +#+BEGIN_SRC haskell :eval never-export + askUser :: IO [Integer] + askUser = do + putStrLn "Enter a list of numbers:" + input <- getLine + let maybeList = getListFromString input in + case maybeList of + Just l -> return l + Nothing -> askUser +#+END_SRC + +After: + +#+BEGIN_SRC haskell :eval never-export + askUser w0 = + let (_,w1) = putStrLn "Enter a list of numbers:" in + let (input,w2) = getLine w1 in + let (l,w3) = case getListFromString input of + Just l -> (l,w2) + Nothing -> askUser w2 + in + (l,w3) +#+END_SRC + +This is similar, but awkward. Look at all these temporary =w?= names. + +The lesson is: naive IO implementation in Pure functional languages is +awkward! + +Fortunately, there is a better way to handle this problem. We see a +pattern. Each line is of the form: + +#+BEGIN_SRC haskell :eval never-export + let (y,w') = action x w in +#+END_SRC + +Even if for some lines the first =x= argument isn't needed. The output +type is a couple, =(answer, newWorldValue)=. Each function =f= must have +a type similar to: + +#+BEGIN_SRC haskell :eval never-export + f :: World -> (a,World) +#+END_SRC + +Not only this, but we can also note that we always follow the same usage +pattern: + +#+BEGIN_SRC haskell :eval never-export + let (y,w1) = action1 w0 in + let (z,w2) = action2 w1 in + let (t,w3) = action3 w2 in + ... +#+END_SRC + +Each action can take from 0 to n parameters. And in particular, each +action can take a parameter from the result of a line above. + +For example, we could also have: + +#+BEGIN_SRC haskell :eval never-export + let (_,w1) = action1 x w0 in + let (z,w2) = action2 w1 in + let (_,w3) = action3 z w2 in + ... +#+END_SRC + +With, of course: =actionN w :: (World) -> (a,World)=. + +#+BEGIN_QUOTE + IMPORTANT: there are only two important patterns to consider: + + #+BEGIN_EXAMPLE + let (x,w1) = action1 w0 in + let (y,w2) = action2 x w1 in + #+END_EXAMPLE + + and + + #+BEGIN_EXAMPLE + let (_,w1) = action1 w0 in + let (y,w2) = action2 w1 in + #+END_EXAMPLE +#+END_QUOTE + +leftblogimage("jocker_pencil_trick.jpg","Jocker pencil trick") + +Now, we will do a magic trick. We will make the temporary world symbols +"disappear". We will =bind= the two lines. Let's define the =bind= +function. Its type is quite intimidating at first: + +#+BEGIN_SRC haskell :eval never-export + bind :: (World -> (a,World)) + -> (a -> (World -> (b,World))) + -> (World -> (b,World)) +#+END_SRC + +But remember that =(World -> (a,World))= is the type for an IO action. +Now let's rename it for clarity: + +#+BEGIN_SRC haskell :eval never-export + type IO a = World -> (a, World) +#+END_SRC + +Some examples of functions: + +#+BEGIN_SRC haskell :eval never-export + getLine :: IO String + print :: Show a => a -> IO () +#+END_SRC + +=getLine= is an IO action which takes world as a parameter and returns a +couple =(String, World)=. This can be summarized as: =getLine= is of +type =IO String=, which we also see as an IO action which will return a +String "embeded inside an IO". + +The function =print= is also interesting. It takes one argument which +can be shown. In fact it takes two arguments. The first is the value to +print and the other is the state of world. It then returns a couple of +type =((), World)=. This means that it changes the state of the world, +but doesn't yield any more data. + +This new =IO a= type helps us simplify the type of =bind=: + +#+BEGIN_SRC haskell :eval never-export + bind :: IO a + -> (a -> IO b) + -> IO b +#+END_SRC + +It says that =bind= takes two IO actions as parameters and returns +another IO action. + +Now, remember the /important/ patterns. The first was: + +#+BEGIN_SRC haskell :eval never-export + pattern1 w0 = + let (x,w1) = action1 w0 in + let (y,w2) = action2 x w1 in + (y,w2) +#+END_SRC + +Look at the types: + +#+BEGIN_SRC haskell :eval never-export + action1 :: IO a + action2 :: a -> IO b + pattern1 :: IO b +#+END_SRC + +Doesn't it seem familiar? + +#+BEGIN_SRC haskell :eval never-export + (bind action1 action2) w0 = + let (x, w1) = action1 w0 + (y, w2) = action2 x w1 + in (y, w2) +#+END_SRC + +The idea is to hide the World argument with this function. Let's go: As +an example imagine if we wanted to simulate: + +#+BEGIN_SRC haskell :eval never-export + let (line1, w1) = getLine w0 in + let ((), w2) = print line1 in + ((), w2) +#+END_SRC + +Now, using the =bind= function: + +#+BEGIN_SRC haskell :eval never-export + (res, w2) = (bind getLine print) w0 +#+END_SRC + +As print is of type =Show a => a -> (World -> ((), World))=, we know +=res = ()= (=unit= type). If you didn't see what was magic here, let's +try with three lines this time. + +#+BEGIN_SRC haskell :eval never-export + let (line1,w1) = getLine w0 in + let (line2,w2) = getLine w1 in + let ((),w3) = print (line1 ++ line2) in + ((),w3) +#+END_SRC + +Which is equivalent to: + +#+BEGIN_SRC haskell :eval never-export + (res,w3) = (bind getLine (\line1 -> + (bind getLine (\line2 -> + print (line1 ++ line2))))) w0 +#+END_SRC + +Didn't you notice something? Yes, no temporary World variables are used +anywhere! This is /MA/. /GIC/. + +We can use a better notation. Let's use =(>>=)= instead of =bind=. +=(>>=)= is an infix function like =(+)=; reminder =3 + 4 ⇔ (+) 3 4= + +#+BEGIN_SRC haskell :eval never-export + (res,w3) = (getLine >>= + (\line1 -> getLine >>= + (\line2 -> print (line1 ++ line2)))) w0 +#+END_SRC + +fr; Haskell a confectionné du sucre syntaxique pour vous : Ho Ho Ho! +Merry Christmas Everyone! Haskell has made syntactical sugar for us: + +#+BEGIN_SRC haskell :eval never-export + do + x <- action1 + y <- action2 + z <- action3 + ... +#+END_SRC + +Is replaced by: + +#+BEGIN_SRC haskell :eval never-export + action1 >>= (\x -> + action2 >>= (\y -> + action3 >>= (\z -> + ... + ))) +#+END_SRC + +Note that you can use =x= in =action2= and =x= and =y= in =action3=. + +But what about the lines not using the =<-=? Easy, another function +=blindBind=: + +#+BEGIN_SRC haskell :eval never-export + blindBind :: IO a -> IO b -> IO b + blindBind action1 action2 w0 = + bind action (\_ -> action2) w0 +#+END_SRC + +I didn't simplify this definition for the purposes of clarity. Of +course, we can use a better notation: we'll use the =(>>)= operator. + +And + +#+BEGIN_SRC haskell :eval never-export + do + action1 + action2 + action3 +#+END_SRC + +Is transformed into + +#+BEGIN_SRC haskell :eval never-export + action1 >> + action2 >> + action3 +#+END_SRC + +Also, another function is quite useful. + +#+BEGIN_SRC haskell :eval never-export + putInIO :: a -> IO a + putInIO x = IO (\w -> (x,w)) +#+END_SRC + +This is the general way to put pure values inside the "IO context". The +general name for =putInIO= is =return=. This is quite a bad name when +you learn Haskell. =return= is very different from what you might be +used to. + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/01_IO/21_Detailled_IO.lhs + +To finish, let's translate our example: + +#+BEGIN_SRC haskell :eval never-export + + askUser :: IO [Integer] + askUser = do + putStrLn "Enter a list of numbers (separated by commas):" + input <- getLine + let maybeList = getListFromString input in + case maybeList of + Just l -> return l + Nothing -> askUser + + main :: IO () + main = do + list <- askUser + print $ sum list +#+END_SRC + +Is translated into: + +#+BEGIN_SRC haskell :eval never-export + import Data.Maybe + + maybeRead :: Read a => String -> Maybe a + maybeRead s = case reads s of + [(x,"")] -> Just x + _ -> Nothing + getListFromString :: String -> Maybe [Integer] + getListFromString str = maybeRead $ "[" ++ str ++ "]" + askUser :: IO [Integer] + askUser = + putStrLn "Enter a list of numbers (sep. by commas):" >> + getLine >>= \input -> + let maybeList = getListFromString input in + case maybeList of + Just l -> return l + Nothing -> askUser + + main :: IO () + main = askUser >>= + \list -> print $ sum list +#+END_SRC + +You can compile this code to verify that it works. + +Imagine what it would look like without the =(>>)= and =(>>=)=. + +03_Hell/01_IO/21_Detailled_IO.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/02_Monads/10_Monads.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("dali_reve.jpg","Dali, reve. It represents a weapon out of the +mouth of a tiger, itself out of the mouth of another tiger, itself out +of the mouth of a fish itself out of a grenade. I could have choosen a +picture of the Human centipede as it is a very good representation of +what a monad really is. But just to think about it, I find this +disgusting and that wasn't the purpose of this document.") + +Now the secret can be revealed: =IO= is a /monad/. Being a monad means +you have access to some syntactical sugar with the =do= notation. But +mainly, you have access to a coding pattern which will ease the flow of +your code. + +#+BEGIN_QUOTE + *Important remarks*: + + - Monad are not necessarily about effects! There are a lot of /pure/ + monads. + - Monad are more about sequencing +#+END_QUOTE + +In Haskell, =Monad= is a type class. To be an instance of this type +class, you must provide the functions =(>>=)= and =return=. The function +=(>>)= is derived from =(>>=)=. Here is how the type class =Monad= is +declared (basically): + +#+BEGIN_SRC haskell :eval never-export + class Monad m where + (>>=) :: m a -> (a -> m b) -> m b + return :: a -> m a + + (>>) :: m a -> m b -> m b + f >> g = f >>= \_ -> g + + -- You should generally safely ignore this function + -- which I believe exists for historical reasons + fail :: String -> m a + fail = error +#+END_SRC + +#+BEGIN_QUOTE + Remarks: + + - the keyword =class= is not your friend. A Haskell class is /not/ a + class of the kind you will find in object-oriented programming. A + Haskell class has a lot of similarities with Java interfaces. A + better word would have been =typeclass=, since that means a set of + types. For a type to belong to a class, all functions of the class + must be provided for this type. + - In this particular example of type class, the type =m= must be a + type that takes an argument. for example =IO a=, but also =Maybe a=, + =[a]=, etc... + - To be a useful monad, your function must obey some rules. If your + construction does not obey these rules strange things might happens: +#+END_QUOTE + +#+BEGIN_QUOTE + #+BEGIN_EXAMPLE + return a >>= k == k a + m >>= return == m + m >>= (\x -> k x >>= h) == (m >>= k) >>= h + #+END_EXAMPLE +#+END_QUOTE + +#+BEGIN_HTML +

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

    +#+END_HTML + +There are a lot of different types that are instances of =Monad=. One of +the easiest to describe is =Maybe=. If you have a sequence of =Maybe= +values, you can use monads to manipulate them. It is particularly useful +to remove very deep =if..then..else..= constructions. + +Imagine a complex bank operation. You are eligible to gain about 700€ +only if you can afford to follow a list of operations without your +balance dipping below zero. + +#+BEGIN_SRC haskell :eval never-export + deposit value account = account + value + withdraw value account = account - value + + eligible :: (Num a,Ord a) => a -> Bool + eligible account = + let account1 = deposit 100 account in + if (account1 < 0) + then False + else + let account2 = withdraw 200 account1 in + if (account2 < 0) + then False + else + let account3 = deposit 100 account2 in + if (account3 < 0) + then False + else + let account4 = withdraw 300 account3 in + if (account4 < 0) + then False + else + let account5 = deposit 1000 account4 in + if (account5 < 0) + then False + else + True + + main = do + print $ eligible 300 -- True + print $ eligible 299 -- False +#+END_SRC + +03_Hell/02_Monads/10_Monads.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/02_Monads/11_Monads.lhs + +Now, let's make it better using Maybe and the fact that it is a Monad + +#+BEGIN_SRC haskell :eval never-export + deposit :: (Num a) => a -> a -> Maybe a + deposit value account = Just (account + value) + + withdraw :: (Num a,Ord a) => a -> a -> Maybe a + withdraw value account = if (account < value) + then Nothing + else Just (account - value) + + eligible :: (Num a, Ord a) => a -> Maybe Bool + eligible account = do + account1 <- deposit 100 account + account2 <- withdraw 200 account1 + account3 <- deposit 100 account2 + account4 <- withdraw 300 account3 + account5 <- deposit 1000 account4 + Just True + + main = do + print $ eligible 300 -- Just True + print $ eligible 299 -- Nothing +#+END_SRC + +03_Hell/02_Monads/11_Monads.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/02_Monads/12_Monads.lhs + +Not bad, but we can make it even better: + +#+BEGIN_SRC haskell :eval never-export + deposit :: (Num a) => a -> a -> Maybe a + deposit value account = Just (account + value) + + withdraw :: (Num a,Ord a) => a -> a -> Maybe a + withdraw value account = if (account < value) + then Nothing + else Just (account - value) + + eligible :: (Num a, Ord a) => a -> Maybe Bool + eligible account = + deposit 100 account >>= + withdraw 200 >>= + deposit 100 >>= + withdraw 300 >>= + deposit 1000 >> + return True + + main = do + print $ eligible 300 -- Just True + print $ eligible 299 -- Nothing +#+END_SRC + +We have proven that Monads are a good way to make our code more elegant. +Note this idea of code organization, in particular for =Maybe= can be +used in most imperative languages. In fact, this is the kind of +construction we make naturally. + +#+BEGIN_QUOTE + An important remark: + + The first element in the sequence being evaluated to =Nothing= will + stop the complete evaluation. This means you don't execute all lines. + You get this for free, thanks to laziness. +#+END_QUOTE + +You could also replay these example with the definition of =(>>=)= for +=Maybe= in mind: + +#+BEGIN_SRC haskell :eval never-export + instance Monad Maybe where + (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b + Nothing >>= _ = Nothing + (Just x) >>= f = f x + + return x = Just x +#+END_SRC + +The =Maybe= monad proved to be useful while being a very simple example. +We saw the utility of the =IO= monad. But now for a cooler example, +lists. + +03_Hell/02_Monads/12_Monads.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +03_Hell/02_Monads/13_Monads.lhs + +#+BEGIN_HTML +

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

    +#+END_HTML + +blogimage("golconde.jpg","Golconde de Magritte") + +The list monad helps us to simulate non-deterministic computations. Here +we go: + +#+BEGIN_SRC haskell :eval never-export + import Control.Monad (guard) + + allCases = [1..10] + + resolve :: [(Int,Int,Int)] + resolve = do + x <- allCases + y <- allCases + z <- allCases + guard $ 4*x + 2*y < z + return (x,y,z) + + main = do + print resolve +#+END_SRC + +MA. GIC. : + +#+BEGIN_EXAMPLE + [(1,1,7),(1,1,8),(1,1,9),(1,1,10),(1,2,9),(1,2,10)] +#+END_EXAMPLE + +For the list monad, there is also this syntactic sugar: + +#+BEGIN_SRC haskell :eval never-export + print $ [ (x,y,z) | x <- allCases, + y <- allCases, + z <- allCases, + 4*x + 2*y < z ] +#+END_SRC + +I won't list all the monads, since there are many of them. Using monads +simplifies the manipulation of several notions in pure languages. In +particular, monads are very useful for: + +- IO, +- non-deterministic computation, +- generating pseudo random numbers, +- keeping configuration state, +- writing state, +- ... + +If you have followed me until here, then you've done it! You know +monads[fn:7]! + +03_Hell/02_Monads/13_Monads.lhs + +#+BEGIN_HTML +

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

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

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

    +#+END_HTML + +In the section [[#infinite-structures][Infinite Structures]] we saw some +simple constructions. Unfortunately we removed two properties from our +tree: + +1. no duplicate node value +2. well ordered tree + +In this section we will try to keep the first property. Concerning the +second one, we must relax it but we'll discuss how to keep it as much as +possible. + +This code is mostly the same as the one in the [[#trees][tree section]]. + +#+BEGIN_SRC haskell :eval never-export + import Data.List + data BinTree a = Empty + | Node a (BinTree a) (BinTree a) + deriving (Eq,Ord) + + -- declare BinTree a to be an instance of Show + instance (Show a) => Show (BinTree a) where + -- will start by a '<' before the root + -- and put a : a begining of line + show t = "< " ++ replace '\n' "\n: " (treeshow "" t) + where + treeshow pref Empty = "" + treeshow pref (Node x Empty Empty) = + (pshow pref x) + + treeshow pref (Node x left Empty) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " left) + + treeshow pref (Node x Empty right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " right) + + treeshow pref (Node x left right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "|--" "| " left) ++ "\n" ++ + (showSon pref "`--" " " right) + + -- show a tree using some prefixes to make it nice + showSon pref before next t = + pref ++ before ++ treeshow (pref ++ next) t + + -- pshow replace "\n" by "\n"++pref + pshow pref x = replace '\n' ("\n"++pref) (show x) + + -- replace on char by another string + replace c new string = + concatMap (change c new) string + where + change c new x + | x == c = new + | otherwise = x:[] -- "x" +#+END_SRC + +Our first step is to create some pseudo-random number list: + +#+BEGIN_SRC haskell :eval never-export + shuffle = map (\x -> (x*3123) `mod` 4331) [1..] +#+END_SRC + +Just as a reminder, here is the definition of =treeFromList= + +#+BEGIN_SRC haskell :eval never-export + treeFromList :: (Ord a) => [a] -> BinTree a + treeFromList [] = Empty + treeFromList (x:xs) = Node x (treeFromList (filter (x) xs)) +#+END_SRC + +and =treeTakeDepth=: + +#+BEGIN_SRC haskell :eval never-export + treeTakeDepth _ Empty = Empty + treeTakeDepth 0 _ = Empty + treeTakeDepth n (Node x left right) = let + nl = treeTakeDepth (n-1) left + nr = treeTakeDepth (n-1) right + in + Node x nl nr +#+END_SRC + +See the result of: + +#+BEGIN_SRC haskell :eval never-export + main = do + putStrLn "take 10 shuffle" + print $ take 10 shuffle + putStrLn "\ntreeTakeDepth 4 (treeFromList shuffle)" + print $ treeTakeDepth 4 (treeFromList shuffle) +#+END_SRC + +#+BEGIN_EXAMPLE + % runghc 02_Hard_Part/41_Infinites_Structures.lhs + take 10 shuffle + [3123,1915,707,3830,2622,1414,206,3329,2121,913] + treeTakeDepth 4 (treeFromList shuffle) + + < 3123 + : |--1915 + : | |--707 + : | | |--206 + : | | `--1414 + : | `--2622 + : | |--2121 + : | `--2828 + : `--3830 + : |--3329 + : | |--3240 + : | `--3535 + : `--4036 + : |--3947 + : `--4242 +#+END_EXAMPLE + +Yay! It ends! Beware though, it will only work if you always have +something to put into a branch. + +For example + +#+BEGIN_SRC haskell :eval never-export + treeTakeDepth 4 (treeFromList [1..]) +#+END_SRC + +will loop forever. Simply because it will try to access the head of +=filter (<1) [2..]=. But =filter= is not smart enought to understand +that the result is the empty list. + +Nonetheless, it is still a very cool example of what non strict programs +have to offer. + +Left as an exercise to the reader: + +- Prove the existence of a number =n= so that + =treeTakeDepth n (treeFromList shuffle)= will enter an infinite loop. +- Find an upper bound for =n=. +- Prove there is no =shuffle= list so that, for any depth, the program + ends. + +04_Appendice/01_More_on_infinite_trees/10_Infinite_Trees.lhs + +#+BEGIN_HTML +
    +#+END_HTML + +04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs + +This code is mostly the same as the preceding one. + +#+BEGIN_SRC haskell :eval never-export + import Debug.Trace (trace) + import Data.List + data BinTree a = Empty + | Node a (BinTree a) (BinTree a) + deriving (Eq,Ord) +#+END_SRC + +#+BEGIN_SRC haskell :eval never-export + -- declare BinTree a to be an instance of Show + instance (Show a) => Show (BinTree a) where + -- will start by a '<' before the root + -- and put a : a begining of line + show t = "< " ++ replace '\n' "\n: " (treeshow "" t) + where + treeshow pref Empty = "" + treeshow pref (Node x Empty Empty) = + (pshow pref x) + + treeshow pref (Node x left Empty) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " left) + + treeshow pref (Node x Empty right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "`--" " " right) + + treeshow pref (Node x left right) = + (pshow pref x) ++ "\n" ++ + (showSon pref "|--" "| " left) ++ "\n" ++ + (showSon pref "`--" " " right) + + -- show a tree using some prefixes to make it nice + showSon pref before next t = + pref ++ before ++ treeshow (pref ++ next) t + + -- pshow replace "\n" by "\n"++pref + pshow pref x = replace '\n' ("\n"++pref) (" " ++ show x) + + -- replace on char by another string + replace c new string = + concatMap (change c new) string + where + change c new x + | x == c = new + | otherwise = x:[] -- "x" + + treeTakeDepth _ Empty = Empty + treeTakeDepth 0 _ = Empty + treeTakeDepth n (Node x left right) = let + nl = treeTakeDepth (n-1) left + nr = treeTakeDepth (n-1) right + in + Node x nl nr +#+END_SRC + +In order to resolve these problem we will modify slightly our +=treeFromList= and =shuffle= function. + +A first problem, is the lack of infinite different number in our +implementation of =shuffle=. We generated only =4331= different numbers. +To resolve this we make a slightly better =shuffle= function. + +#+BEGIN_SRC haskell :eval never-export + shuffle = map rand [1..] + where + rand x = ((p x) `mod` (x+c)) - ((x+c) `div` 2) + p x = m*x^2 + n*x + o -- some polynome + m = 3123 + n = 31 + o = 7641 + c = 1237 +#+END_SRC + +This shuffle function has the property (hopefully) not to have an upper +nor lower bound. But having a better shuffle list isn't enough not to +enter an infinite loop. + +Generally, we cannot decide whether =filter ( [a] -> BinTree a + treeFromList [] = Empty + treeFromList (x:xs) = Node x left right + where + left = treeFromList $ safefilter (x) xs +#+END_SRC + +This new function =safefilter= is almost equivalent to =filter= but +don't enter infinite loop if the result is a finite list. If it cannot +find an element for which the test is true after 10000 consecutive +steps, then it considers to be the end of the search. + +#+BEGIN_SRC haskell :eval never-export + safefilter :: (a -> Bool) -> [a] -> [a] + safefilter f l = safefilter' f l nbTry + where + nbTry = 10000 + safefilter' _ _ 0 = [] + safefilter' _ [] _ = [] + safefilter' f (x:xs) n = + if f x + then x : safefilter' f xs nbTry + else safefilter' f xs (n-1) +#+END_SRC + +Now run the program and be happy: + +#+BEGIN_SRC haskell :eval never-export + main = do + putStrLn "take 10 shuffle" + print $ take 10 shuffle + putStrLn "\ntreeTakeDepth 8 (treeFromList shuffle)" + print $ treeTakeDepth 8 (treeFromList $ shuffle) +#+END_SRC + +You should realize the time to print each value is different. This is +because Haskell compute each value when it needs it. And in this case, +this is when asked to print it on the screen. + +Impressively enough, try to replace the depth from =8= to =100=. It will +work without killing your RAM! The flow and the memory management is +done naturally by Haskell. + +Left as an exercise to the reader: + +- Even with large constant value for =deep= and =nbTry=, it seems to + work nicely. But in the worst case, it can be exponential. Create a + worst case list to give as parameter to =treeFromList=.\\ + /hint/: think about (=[0,-1,-1,....,-1,1,-1,...,-1,1,...]=). +- I first tried to implement =safefilter= as follow: + + #+BEGIN_HTML +
    +    safefilter' f l = if filter f (take 10000 l) == []
    +                      then []
    +                      else filter f l
    +    
    + #+END_HTML + + Explain why it doesn't work and can enter into an infinite loop. +- Suppose that =shuffle= is real random list with growing bounds. If you + study a bit this structure, you'll discover that with probability 1, + this structure is finite. Using the following code (suppose we could + use =safefilter'= directly as if was not in the where of safefilter) + find a definition of =f= such that with probability =1=, + =treeFromList' shuffle= is infinite. And prove it. Disclaimer, this is + only a conjecture. + +#+BEGIN_SRC haskell :eval never-export + treeFromList' [] n = Empty + treeFromList' (x:xs) n = Node x left right + where + left = treeFromList' (safefilter' (x) xs (f n) + f = ??? +#+END_SRC + +04_Appendice/01_More_on_infinite_trees/11_Infinite_Trees.lhs + +** Thanks + :PROPERTIES: + :CUSTOM_ID: thanks + :END: + +Thanks to [[http://reddit.com/r/haskell][=/r/haskell=]] and +[[http://reddit.com/r/programming][=/r/programming=]]. Your comment were +most than welcome. + +Particularly, I want to thank [[https://github.com/Emm][Emm]] a thousand +times for the time he spent on correcting my English. Thank you man. + +[fn:1] Even if most recent languages try to hide them, they are present. + +[fn:2] I know I'm cheating. But I will talk about non-strictness later. + +[fn:3] For the brave, a more complete explanation of pattern matching + can be found + [[http://www.cs.auckland.ac.nz/references/haskell/haskell-intro-html/patterns.html][here]]. + +[fn:4] Which is itself very similar to the javascript =eval= function, + that is applied to a string containing JSON. + +[fn:5] There are some /unsafe/ exceptions to this rule. But you + shouldn't see such use in a real application except maybe for + debugging purposes. + +[fn:6] For the curious ones, the real type is + =data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}=. + All the =#= has to do with optimisation and I swapped the fields + in my example. But this is the basic idea. + +[fn:7] Well, you'll certainly need to practice a bit to get used to them + and to understand when you can use them and create your own. But + you already made a big step in this direction. diff --git a/src/drafts/blog-detoxify-web.org b/src/drafts/blog-detoxify-web.org new file mode 100644 index 0000000..c8413ac --- /dev/null +++ b/src/drafts/blog-detoxify-web.org @@ -0,0 +1,39 @@ +#+TITLE: Detoxify the Web +#+SUBTITLE: Take back control of what we are exposed to on the web +#+AUTHOR: Yann Esposito + +We are currently in an age were where you take your eyeball is a valuable resource. +As such there are professional, and a lot of smart ones, whose only job is to ensure +you pass more time looking at what there is best in their interrest, not yours. + +There are a lot of techniques to grab your attention and not letting it go somewhere else. + +- animation +- colors +- gaming the relationship +- entertaining + +All of them are in fact not really problematic. But now, this is too much. +So in order to feel calm again we need to have better control of what we see, are exposed to. + +If you take a look into the gopher sphere, you discover a lot text-oriented world. +Of course there are pictures, but not in the middle of the article. + +So mostly text. No gif, no animation, no music, no video, just textual content. + +We passed from a world where there was too much textual content, to a world filled +with meaningless animations, colors, pictures, effects, popups, etc... + +*Detoxify the web* + +Here is my solution: + +- remove all the styles of all the websites so a lot of website cannot be used anymore that way. +- replace by a default focus oriented style. + +My only solution is to start a proxy between me and the web, I take the content and do a lot of cleanup. +I remove all the CSS (faster), I use my own (nicer and contolled) + +Simple and efficient. + +This transformation should be done by the client. diff --git a/src/drafts/sad-text-browsing.org b/src/drafts/sad-text-browsing.org new file mode 100644 index 0000000..aa6fd6c --- /dev/null +++ b/src/drafts/sad-text-browsing.org @@ -0,0 +1,13 @@ +#+TITLE: The sad state of text browsing +#+AUTHOR: Yann Esposito + +1. sad state, show HN, Reddit, worse, news websites, etc... +2. bad for disabled people +3. why not change the concept and make the provider of content not also provide the Look & Feel + User controlled display of content. Less money, more quality, is it possible + - no js + - text only + - interaction? controlled one + - user facing code, yes, only signature, all the rest looks like bullshit to me +4. not web 3.0, more like, fuck it, we need a BRUTALIST, Hacker-only oriented + web. A safe place to talk between civilised people. What are the possible solutions diff --git a/src/index.org b/src/index.org index 637c764..e0c267f 100644 --- a/src/index.org +++ b/src/index.org @@ -8,21 +8,8 @@ #+OPTIONS: H:5 #+STARTUP: showeverything -This is a new take on my personal blog. -I tried to have some minimalism in mind but not too much. +Welcome to my personal blog. +This is the 4th time I change the underlying technology behind my blog. -True minimalism would have been to only serve text files. -But I would prefer to have at least links working. Also -syntax highlighting of source code is nice. - -But I dislike the new trend of design space and white colors. -It doesn't really fit the spirit of my website. -So it looks a bit more like a text into a colored terminal. - -The CSS support dark/light theme. -If unknown, the default color scheme is dark in the hope to save a bit of energy -from screen. -You can have a basic demo about most supported content styles/type (paragraphs, -lists, tables, etc...) [[file:./demo.org][here]]. - -I write all the content with [[http://orgmode.org][org-mode]]. +- [[./archive.html][posts]] +- [[./about][about me]] diff --git a/src/posts/2019-07-04-static-org-publish.org b/src/posts/2019-07-04-static-org-publish.org index e815e53..c2ccc8f 100644 --- a/src/posts/2019-07-04-static-org-publish.org +++ b/src/posts/2019-07-04-static-org-publish.org @@ -1,15 +1,15 @@ #+TITLE: Static blog with org-mode #+AUTHOR: Yann Esposito #+EMAIL: yann.esposito@gmail.com -#+DATE: 2019-07-04 +#+DATE: 2019-07-27 #+KEYWORDS: programming, blog, org-mode #+OPTIONS: auto-id:t #+begin_quote -/tl;dr/: [[#current-solution-0a92][Full code for the impatient↓]]. Read the full blog post to have all the details. +/tl;dr/: [[#current-solution][Full code for the impatient↓]]. +Read the full blog post to have all the details. #+end_quote - This is the first article using my new blog system. Once in a while, I create a new personal website. @@ -63,6 +63,11 @@ You can add a task quite easily while doing other work in emacs. :CUSTOM_ID: how--831e :END: +You can easily follow the org-mode doc to make your own website. +But I have added a few "goodies". + +- attempt to obfuscate contact email, + ** Basic Blog :PROPERTIES: :CUSTOM_ID: basic-blog-a1fc @@ -70,8 +75,7 @@ You can add a task quite easily while doing other work in emacs. I put the need minimal code in a =.project.el.gpg= file of my blog repository. Inspired by this [[https://francismurillo.github.io/2017-02-15-Project-Script-Loader/][blog post]] I enhanced this version to be both more user friendly -and secure (see [[#digression]]). - +and secure (see [[./autoload-emacs-script-by-project.org][Autoload Emacs Script by Project]]). But even before that, I simply put the code in has an elisp code block of my =index.org= that I exectued with =C-c C-c=. @@ -188,7 +192,7 @@ That's better. But for a blog you also generally want to have your own CSS. * Current Solution :PROPERTIES: - :CUSTOM_ID: current-solution-0a92 + :CUSTOM_ID: current-solution :END: #+begin_src elisp @@ -201,7 +205,8 @@ That's better. But for a blog you also generally want to have your own CSS. (setq rss-dir base-dir) (setq publish-rss-dir publish-dir) (setq css-name "minimalist.css") - + (setq author-name "Yann Esposito") + (setq author-email "yann.esposito@gmail.com") (require 'org) (require 'ox-publish) @@ -211,8 +216,8 @@ That's better. But for a blog you also generally want to have your own CSS. (defun org-blog-prepare (project-plist) "With help from `https://github.com/howardabrams/dot-files'. - Touch `index.org' to rebuilt it. - Argument `PROJECT-PLIST' contains information about the current project." + Touch `index.org' to rebuilt it. + Argument `PROJECT-PLIST' contains information about the current project." (let* ((base-directory (plist-get project-plist :base-directory)) (buffer (find-file-noselect (expand-file-name "index.org" base-directory) t))) (with-current-buffer buffer @@ -224,7 +229,7 @@ That's better. But for a blog you also generally want to have your own CSS. "" "" "" - "")) + "")) (defun menu (lst) "Blog menu" @@ -320,8 +325,8 @@ That's better. But for a blog you also generally want to have your own CSS. (defun org-blog-sitemap-function (title list) "Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'." (concat "#+TITLE: " title "\n" - "#+AUTHOR: Yann Esposito\n" - "#+EMAIL: yann.esposito@gmail.com\n" + "#+AUTHOR: " author-name "\n" + "#+EMAIL: " author-email "\n" "\n#+begin_archive\n" (mapconcat (lambda (li) (format "@@html:
  • @@ %s @@html:
  • @@" (car li))) @@ -399,96 +404,3 @@ That's better. But for a blog you also generally want to have your own CSS. 'my-org-export-add-target-blank-to-http-links) #+end_src -* Digression - :PROPERTIES: - :CUSTOM_ID: digression - :END: - -Auto loading a =.el= file when entering in a project. -It should be easy and safe. - -The best solution I found. -Save your =.el= file as a =.el.gpg= file. -With =epa= emacs should encrypt the file for you using your own private key. -Great! Now we simply verify the file exists and load it only if it is encrypted -with one of you /trusted keys/. -The list of key fingerprint you trust is a configuration. - -When opening a new file via projectile or dired - -1. check the =.project.el.gpg= file is not already loaded for the current project -2. check the =.project.el.gpg= file exists -3. check the =.project.el.gpg= file is encrypted with a trusted key -4. decrypt and load =.project.el.gpg= - -That's it. - -You simply need to set the =y/trusted-gpg-key-fingerprints= variable with the -list of your own fingerprint. -You can get a list of them with =gpg --list-secret-keys=. - -Now you can be happy, this is really safe, in the sense that if you clone a new -project from the internet with a =.project.el.gpg= file in it. -That file won't be run on your system. - -Of course if you want to share that =.el= to other people, you need to adapt. -But for my use case, this is perfect. - -Another use case would be to check the signature and trust people. - -#+begin_src elisp - (defvar y/trusted-gpg-key-fingerprints - '("448E9FEF4F5B86DE79C1669B7B19A4C650D59646") - "The list of GPG fingerprint you trust when decrypting a gpg file. - You can retrieve the fingerprints of your own private keys with: - `gpg --list-secret-keys' (take care of removing the spaces when copy/pasting here)") - - (defun y/get-encryption-key (file) - "given a gpg encrypted file, returns the fingerprint of they - key that encrypted it" - (string-trim-right - (shell-command-to-string - (concat - "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" - "|grep DECRYPTION_KEY" - "|awk '{print $4}'")))) - - (defun y/trusted-gpg-origin-p (file) - "Returns true if the file is encrypted with a trusted key" - (member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints)) - - (defun y/project-el-auto-load () - (with-eval-after-load 'projectile - (defconst y/project-file ".project.el.gpg" - "Filename looked after to be loaded and evaluated.") - (defvar y/loaded-projects (list) - "Projects that have been loaded by `y/load-project-file'.") - (defun y/load-project-file () - "Loads the `y/project-file' for a project. This is run - once after the project is loaded signifying project setup." - (interactive) - (when (projectile-project-p) - (lexical-let* ((project-root (projectile-project-root)) - (project-init-file (expand-file-name y/project-file project-root))) - (when (and (not (member project-root y/loaded-projects)) ;; project file not already loaded - (file-exists-p project-init-file) ;; project file exists - (y/trusted-gpg-origin-p project-init-file)) ;; project file is tursted - (message "Loading project init file for %s" (projectile-project-name)) - (condition-case ex - (progn (load project-init-file) - (add-to-list 'y/loaded-projects project-root) - (message "%s loaded successfully" project-init-file)) - ('error - (message - "There was an error loading %s: %s" - project-init-file - (error-message-string ex)))))))) - ;; for some obscure reason there is not really a working hook - ;; to be used with projectile to launch the `y/load-project-file' - ;; only when switching to a new project. - ;; Thus the `y/loaded-projects' state. - (add-hook 'find-file-hook #'y/load-project-file t) - (add-hook 'dired-mode-hook #'y/load-project-file t))) -#+end_src - - diff --git a/src/posts/project-el/auto-load-project.el b/src/posts/project-el/auto-load-project.el new file mode 100644 index 0000000..2bdd2c4 --- /dev/null +++ b/src/posts/project-el/auto-load-project.el @@ -0,0 +1,72 @@ +;;; auto-load-project.el --- Auto load elisp file on project open + +;; Copyright © 2019 Yann Esposito + +;;; Commentary: +;; + +;;; Code: + +(provide :auto-load-project) + +(require 'projectile) + +(defvar y/trusted-gpg-key-fingerprints + '("448E9FEF4F5B86DE79C1669B0000000000000000") + "The list of GPG fingerprint you trust when decrypting a gpg file. + You can retrieve the fingerprints of your own private keys + with: `gpg --list-secret-keys' (take care of removing the + spaces when copy/pasting here)") + +(defun y/get-encryption-key (file) + "given a gpg encrypted file, returns the fingerprint of + they key that encrypted it" + (string-trim-right + (shell-command-to-string + (concat + "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" + "|grep DECRYPTION_KEY" + "|awk '{print $4}'")))) + +(defun y/trusted-gpg-origin-p (file) + "Returns true if the file is encrypted with a trusted key" + (member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints)) + + +(defconst y/project-file ".project.el.gpg" + "Project configuration file name.") + +(defun y/init-project-el-auto-load () + "Initialize the autoload of .project.el.gpg for projects." + + (with-eval-after-load 'projectile + + (defvar y/loaded-projects (list) + "Projects that have been loaded by `y/load-project-file'.") + + (defun y/load-project-file () + "Loads the `y/project-file' for a project. This is run once + after the project is loaded signifying project setup." + (interactive) + (when (projectile-project-p) + (lexical-let* ((current-project-root (projectile-project-root)) + (project-init-file (expand-file-name y/project-file current-project-root))) + (when (and (not (member current-project-root y/loaded-projects)) + (file-exists-p project-init-file) + (y/trusted-gpg-origin-p project-init-file)) + (message "Loading project init file for %s" (projectile-project-name)) + (condition-case ex + (progn (load project-init-file) + (add-to-list 'y/loaded-projects current-project-root) + (message "%s loaded successfully" project-init-file)) + ('error + (message + "There was an error loading %s: %s" + project-init-file + (error-message-string ex)))))))) + (add-hook 'find-file-hook #'y/load-project-file t) + (add-hook 'dired-mode-hook #'y/load-project-file t))) + +(provide 'auto-load-project) + +;;; auto-load-project.el ends here diff --git a/src/posts/project-el/index.org b/src/posts/project-el/index.org new file mode 100644 index 0000000..a9e45e8 --- /dev/null +++ b/src/posts/project-el/index.org @@ -0,0 +1,133 @@ +#+TITLE: Autoload Script by project +#+SUBTITLE: fast, secure, easy autoload +#+AUTHOR: Yann Esposito +#+EMAIL: yann.esposito@gmail.com +#+DATE: 2019-07-28 +#+KEYWORDS: programming, blog, org-mode +#+OPTIONS: auto-id:t + +#+begin_quote +/tl;dr/: A script that use projectile and GPG to securely load +an emacs lisp script when opening a new project. + +Check the [[#solution]] section to get the code. +#+end_quote + +* Problem + :PROPERTIES: + :CUSTOM_ID: problem + :END: + +When providing a repository containing only org files of my blog. +I also wanted to provide everything necessary for users to be able to publish my +website. +Emacs, org-publish mostly assume you should put all those details in a +centralised place in your =~/.emacs.d/.init.el= file. + +The main principle is quite simple. + +1. When finding a new file in a project, check for the presence of a + =.project.el.gpg= file. +2. Check the file was encrypted with by a trusted fingerprint +3. load the file + +I found a solution that asked you each time you enter in a project if you trust +it. +This was both quite annoying and insecure as it is kind of easy to type 'y' +instead of 'n' and to load an untrusted 3rd party script. + +With emacs epa, opening encrypted gpg files is seamless. +But here we not only want to open the gpg file but also check we trust the +person that did it. + +Note that checking who's encrypted a gpg file is not straightforward: + +#+begin_src elisp +(defun y/get-encryption-key (file) + "given a gpg encrypted file, returns the fingerprint of they + key that encrypted it" + (string-trim-right + (shell-command-to-string + (concat + "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" + "|grep DECRYPTION_KEY" + "|awk '{print $4}'")))) +#+end_src + +- The `--status-fd` should provide more script friendly output. + GPG provide localized output by default which are therefore hard to use in + script (for grep for example). +- we do not want to decrypt to a file so we redirect the output to =/dev/null= + +We use =projectile= to detect the project-root and when we are in a new project. +Unfortunately the =projectile-after-switch-project-hooks= doesn't work as I +expected. +So I use the hooks =find-file-hook= and =dired-mode-hook= to try to load the +file. +In order not to load the code each time, I need to keep a local state of project +already loaded. + +* Solution + :PROPERTIES: + :CUSTOM_ID: solution + :END: + +#+begin_src elisp + (defun y/init-project-el-auto-load () + "Initialize the autoload of .project.el.gpg for projects" + + (with-eval-after-load 'projectile + + (defvar y/trusted-gpg-key-fingerprints + '("448E9FEF4F5B86DE79C1669B0000000000000000") + "The list of GPG fingerprint you trust when decrypting a gpg file. + You can retrieve the fingerprints of your own private keys + with: `gpg --list-secret-keys' (take care of removing the + spaces when copy/pasting here)") + + (defun y/get-encryption-key (file) + "given a gpg encrypted file, returns the fingerprint of + they key that encrypted it" + (string-trim-right + (shell-command-to-string + (concat + "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" + "|grep DECRYPTION_KEY" + "|awk '{print $4}'")))) + + (defun y/trusted-gpg-origin-p (file) + "Returns true if the file is encrypted with a trusted key" + (member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints)) + + + (defconst y/project-file ".project.el.gpg" + "Project configuration file name.") + + (defvar y/loaded-projects (list) + "Projects that have been loaded by `y/load-project-file'.") + + (defun y/load-project-file () + "Loads the `y/project-file' for a project. This is run once + after the project is loaded signifying project setup." + (interactive) + (when (projectile-project-p) + (lexical-let* ((current-project-root (projectile-project-root)) + (project-init-file (expand-file-name y/project-file current-project-root))) + (when (and (not (member current-project-root y/loaded-projects)) + (file-exists-p project-init-file) + (y/trusted-gpg-origin-p project-init-file)) + (message "Loading project init file for %s" (projectile-project-name)) + (condition-case ex + (progn (load project-init-file) + (add-to-list 'y/loaded-projects current-project-root) + (message "%s loaded successfully" project-init-file)) + ('error + (message + "There was an error loading %s: %s" + project-init-file + (error-message-string ex)))))))) + (add-hook 'find-file-hook #'y/load-project-file t) + (add-hook 'dired-mode-hook #'y/load-project-file t))) + + (y/init-project-el-auto-load) +#+end_src