Sei sulla pagina 1di 15

Haskell web programming

A Yesod tutorial

update: updated for yesod 0.10 tl;dr: A simple yesod tutorial. Yesod is a Haskell web framework. You shouldnt need to know Haskell. Table of content

Before the real start o Install o Initiali e o !onfi"ure "it o #ome last minute words $%ho o Bulletproof& o !leanin" up 'se a better %ss #eparate Handlers
Data.Text

'se templates

(irror A Blo" !on%lusion

)hy Haskell&

Its effi%ien%y *see #nap Ben%hmark & )arp Ben%hmark1+. Haskell is an order of ma"nitude faster than interpreted lan"ua"es like ,uby and -ython..

Haskell is a hi"h le/el lan"ua"e and make it harder to shoot you in the foot than C0 C++ or Java for e1ample. 2ne of the best property of Haskell bein": 3If your pro"ram %ompile it will be /ery %lose to what the pro"rammer intended4. Haskell web frameworks handle parallel tasks perfe%tly. 5or e1ample e/en better than node.6s7.

5rom the pure te%hni%al point of /iew0 Haskell seems to be the perfe%t web de/elopment tool. )eaknesses of Haskell %ertainly wont be te%hni%al:

Hard to "rasp Haskell Hard to find a Haskell pro"rammer 8he Haskell %ommunity is smaller than the %ommunity for /.*/ 8here is no heroku for Haskell *e/en if 9re" )eber did it0 it was more a workaround+.

I wont say these are not important drawba%ks. But0 with Haskell your web appli%ation will ha/e both properties to absorb an impressi/e number of parallel re:uest se%urely and to adapt to %han"e. A%tually there are three main Haskell web frameworks: 1. Happsta%k .. #nap 7. Yesod I dont think there is a real winner between these three framework. 8he %hoi%e I made for yesod is hi"hly sub6e%ti/e. I 6ust lurked a bit and tried some tutorials. I had the feelin" yesod make a better 6ob at helpin" new%omers. 5urthermore0 apparently the yesod team seems the most a%ti/e. 2f %ourse I mi"ht be wron" sin%e it is a matter of feelin".

)hy did I write this arti%le& 8he yesod do%umentation and parti%ularly the book are e1%ellent. But I missed an intermediate tutorial. 8his tutorial wont e1plain all details. I tried to "i/e a step by step of how to start from a fi/e minute tutorial to an almost produ%tion ready ar%hite%ture. 5urthermore e1plainin" somethin" to others is a "reat way to learn. If you are used to Haskell and Yesod0 this tutorial wont learn you mu%h. If you are %ompletely new to Haskell and Yesod it mi"ht hopefully helps you. Also if you find yourself too %onfused by the synta10 it mi"ht helps to read this arti%le ;urin" this tutorial youll install0 initiali e and %onfi"ure your first yesod pro6e%t. 8hen there is a /ery minimal < minutes yesod tutorial to heat up and /erify the awesomeness of yesod. 8hen we will %lean up the < minutes tutorial to use some 3best pra%ti%es4. 5inally there will be a more standard real world e1ample; a minimal blo" system.

Before the real start


Install
8he re%ommended way to install Haskell is to download the Haskell -latform. 2n%e done0 you need to install yesod. 2pen a terminal session and do:
~ cabal update ~ cabal install yesod cabal-dev

8here are few steps but it should take some time to finish.

Initialize
You are now ready to initiali e your first yesod pro6e%t. 2pen a terminal and type:
~ yesod init

$nter your name0 %hoose yosog for the pro6e%t name and enter Yosog for the name of the 5oundation. 5inally %hoose sqlite. >ow0 start the de/elopment %y%le:
~ cd yosog ~ cabal-dev install && yesod --dev devel

8his will %ompile the entire pro6e%t. Be patient it %ould take a while the first time. 2n%e finished a ser/er is laun%hed and you %ould /isit it by %li%kin" this link:
ttp!//local ost!"###

!on"ratulation? Yesod works? >ote: if somethin" is messed up use the followin" %ommand line inside the pro6e%t dire%tory.
$%& -%' dist/* ( cabal-dev install && yesod --dev devel

'ntil the end of the tutorial0 use another terminal and let this one open in a %orner to see what o%%urs.

Configure git
2f %ourse this step is not mandatory for the tutorial but it is a "ood pra%ti%e. !opy this .gitigno%e file into the yosog folder. ."iti"nore
cabal-dev dist .static-cac e static/t&p *.sqlite"

8hen initiali e your "it repository:


~ git init . ~ git add . ~ git co&&it -a -& )*nitial yesod co&&it)

)e are almost ready to start.

Some last minute words


'p until here0 we ha/e a dire%tory %ontainin" a bun%h of files and a lo%al web ser/er listenin" the port 7000. If we modify a file inside this dire%tory0 yesod should try to re%ompile as fast as possible the site. Instead of e1plainin" the role of e/ery file0 lets fo%us only on the important files@dire%tories for this tutorial: 1. .. 7. =.
con'ig/%outes +andle%/ te&plates/ con'ig/&odels

<

2b/iously: is where youll %onfi"ure the map url A !ode. +andle%/ %ontains the files that will %ontain the %ode %alled when a url is a%%essed. te&plates/ %ontains html0 6s and %ss templates. con'ig/&odels is where youll %onfi"ure the persistent ob6e%ts *database tables+.
con'ig/%outes

;urin" this tutorial well modify other files as well0 but we wont e1plore them in detail. Also note0 shell %ommands are e1e%uted in the root dire%tory of your pro6e%t instead spe%ified otherwise. )e are now ready to start?

Echo
8o /erify the :uality of the se%urity of the yesod framework0 lets make a minimal e%ho appli%ation. 9oal: (ake a ser/er that when a%%essed /ec o/,so&e text- should return a web pa"e %ontainin" 3some te1t4 inside an . blo%. In a first time0 we must de%lare the url of the form /ec o/... are meanin"ful. Bets take a look at the file con'ig/%outes:
/static /tatic0 /tatic get/tatic /aut 1ut 0 1ut get1ut /'avicon.ico 2avicon0 34T /%obots.txt 0obots0 34T / +o&e0 34T

)e want to add a route of the form /ec o/,anyt ing- somehow and do some a%tion with this. Add the followin":
/ec o/5/t%ing 4c o0 34T

8his line %ontains three elements: the url pattern0 a handler name0 an http method. I am not parti%ularly fan of the bi" , notation but this is the standard %on/ention. If you sa/e con'ig/%outes0 you should see your terminal in whi%h you laun%hed yesod devel a%ti/ate and %ertainly displayin" an error messa"e.
1pplication. s!".!.! 6ot in scope! 7get4c o08

)hy& #imply be%ause we didnt written the %ode for the handler 4c o0. $dit the file +andle%/+o&e. s and append this:

get4c o0 !! /t%ing -9 +andle% 0ep+t&l get4c o0 t eText : do de'ault;ayout < do ,= a&let>? .95@t eTextA>-

;ont worry if you find all of this a bit %rypti%. In short it 6ust de%lare a fun%tion named get4c o0 with one ar"ument *t eText+ of type #trin". )hen this fun%tion is %alled0 it return a +andle% 0ep+t&l whate/er it is. But mainly this will en%apsulate our e1pe%ted result inside an html te1t. After sa/in" the file0 you should see yesod re%ompile the appli%ation. )hen the %ompilation is finished youll see the messa"e: /ta%ting devel application. >ow you %an /isit: ttp!//local ost!"###/ec o/YesodBC#%ocDsE 8A;A? It works?

Bulletproof

$/en this e1tremely minimal web appli%ation has some impressi/e properties. 5or e1emple0 ima"ine an atta%ker enterin" this url:
ttp!//local ost!"###/ec o/?a9*8& ?sc%ipt9ale%tF)GadE)H(

8he spe%ial %hara%ters are prote%ted for us. A mali%ious user %ould not hide some bad s%ript inside. 8his beha/ior is a dire%t %onse:uen%e of type safety. 8he url strin" is put inside a url type. 8hen the interestin" part in the url is put inside a #trin" type. 8o pass from url type to #trin" type some transformation are made. 5or e1ample0 repla%e all 3BC#4 by spa%e %hara%ters. 8hen to show the #trin" inside an html do%ument0 the strin" is put inside an html type. #ome transformations o%%urs like repla%e 3?4 by 3&lt(4. 8hanks to yesod0 this tedious 6ob is done for us.

) ttp!//local ost!"###/ec o/so&eBC#text?a9) !! I0; J )so&e text?a9) !! /t%ing J )so&e text ?a9) !! +t&l

Yesod is not only fast0 it helps us to remain se%ure. It prote%ts us from many %ommon errors in other paradi"ms. Yes0 I am lookin" at you -H-?

Cleaning up
$/en this /ery minimal e1ample should be enhan%ed. )e will %lean up many details:

'se a "eneral %ss *%leaner than the empty by default+ ;ispat%h handler %ode into different files 'se Data.Text instead of /t%ing -ut our 3/iews4= inside the te&plate dire%tory

!se a better css It is ni%e to note0 the default template is based on html< boilerplate. Bets %han"e the default %ss. Add a file named de'ault-layout.lucius inside the te&plates/ dire%tory %ontainin": defaultElayout.lu%ius
body @ 'ont-'a&ily! +elveticaK sans-se%i'( 'ont-siLe! .Mpx( A 5&ain @ padding! .e&( bo%de%! 5CCC solid Cpx( bo%de%-%adius! Npx( &a%gin! .e&( =idt ! "Oe&( &a%gin! .e& auto( bacDg%ound! 52C2C2C( line- eig t! ..Ne&( colo%! 5"""( A .%equi%ed @ &a%gin! .e& #( A .optional @ &a%gin! .e& #( A label @ =idt ! Me&( display! inline-blocD( A inputK texta%ea @ bacDg%ound! 5212121A texta%ea @ =idt ! COe&( eig t! Pe&(A ul @ list-style! squa%e( A a @ colo%! 51NQ( A a! ove% @ colo%! 5CNM( A a!active @ colo%! 5CNM( A a!visited @ colo%! 5PR"( A

-ersonally I would prefer if su%h a minimal %ss was put with the s%affoldin" tool. I am sure somebody already made su%h a minimal %ss whi%h "i/e the impression the browser handle %orre%tly html without any style applied to it. But I di"ress. Separate Handlers

9enerally you dont want to ha/e all your %ode inside a uni:ue file. 8his is why we will separate our handlers. In a first time %reate a new file +andle%/4c o. s %ontainin":
&odule +andle%.4c o = e%e i&po%t *&po%t get4c o0 !! /t%ing -9 +andle% 0ep+t&l get4c o0 t eText : do de'ault;ayout < do ,= a&let>? .95@t eTextA>-

;o not for"et to remo/e the "et$%ho, fun%tion inside +andle%/+o&e. s. )e must de%lare this new file intoyosog.cabal. Gust after +andle%.+o&e0 add:
+andle%.4c o

)e must also de%lare this new Handler module inside 1pplication. s. Gust after the 3i&po%t +andle%.+o&e40 add:
i&po%t +andle%.4c o

8his is it.
ps: I am sure not so far in the future we %ould simply write yesod add- andle% 4c o to de%lare it and %reate a new handler file. Data.Text

It is a "ood pra%ti%e to use Data.Text instead of /t%ing. 8o de%lare it0 add this import dire%ti/e to 2oundation. s *6ust after the last one+:
i&po%t Data.Text

)e ha/e to modify con'ig/%outes and our handler a%%ordin"ly. ,epla%e 5/t%ing by 5Text in con'ig/%outes:
/ec o/5Text 4c o0 34T

And do the same in +andle%/4c o. s: $%ho.hs


&odule +andle%.4c o = e%e i&po%t *&po%t get4c o0 !! Text -9 +andle% 0ep+t&l get4c o0 t eText : do de'ault;ayout < do ,= a&let>? .95@t eTextA>-

!se templates H

#ome html *more pre%isely hamlet+ is written dire%tly inside our handler. )e should put this part inside another file. !reate the new file te&plates/ec o. a&let %ontainin": e%ho.hamlet
? .9 5@t eTextA

and modify the handler +andle%/4c o. s:


get4c o0 !! Text -9 +andle% 0ep+t&l get4c o0 t eText : do de'ault;ayout < do <F=idget2ile )ec o)H

At this point0 our web appli%ation is stru%tured between different files. Handler are "rouped0 we use Data.Text and our /iews are in templates. It is the time to make a sli"htly more %omple1 e1ample.

"irror

Bets make another minimal appli%ation. You should see a form %ontainin" a te1t field and a /alidation button. )hen you enter some te1t *for e1ample 3Gormun"ad4+ and /alidate0 the ne1t pa"e present you the %ontent and its re/erse appended to it. In our e1ample it should return 3Gormun"adda"numroG4. 5irst0 add a new route:
/&i%%o% Si%%o%0 34T TU/T

8his time the path /&i%%o% will a%%ept 9$8 and -2#8 re:uests. Add the %orrespondin" new Handler file: (irror.hs
&odule +andle%.Si%%o% = e%e i&po%t *&po%t i&po%t quali'ied Data.Text as T

10

getSi%%o%0 !! +andle% 0ep+t&l getSi%%o%0 : do de'ault;ayout < do <F=idget2ile )&i%%o%)H postSi%%o%0 !! +andle% 0ep+t&l postSi%%o%0 : do postedText ?- %un*nputTost < i%eq text2ield )content) de'ault;ayout < do <F=idget2ile )posted)H

;ont for"et to de%lare it inside yosog.cabal and 1pplication. s. )e will need to use the %eve%se fun%tion pro/ided by Data.Text whi%h e1plain the additional import. 8he only new thin" here is the line that "et the -2#8 parameter named 3%ontent4. If you want to know more detail about it and form in "eneral you %an take look at the yesod book. !reate the two %orrespondin" templates: mirror.hamlet
? .9 4nte% you% text ?'o%& &et od:post action:V@Si%%o%0A9 ?input type:text na&e:content9 ?input type:sub&it9

posted.hamlet
? .9You8ve Wust posted ?p95@postedTextA5@T.%eve%se postedTextA ? %9 ?p9?a %e':V@Si%%o%0A93et bacD

And that is all. 8his time0 we wont need to %lean up. )e may ha/e used another way to "enerate the form but well see this in the ne1t se%tion. Gust try it by %li%kin" here. Also you %an try to enter stran"e /alues. Bike before0 your appli%ation is :uite se%ure.

A Blog
)e saw how to retrie/e http parameters. It is the time to sa/e thin"s into a database. As before add some routes inside con'ig/%outes:
/blog /blog/51%ticle*d Glog0 1%ticle0 34T TU/T 34T

8his e1ample will be /ery minimal:


34T on /blog should display the list of arti%les. TU/T on /blog should %reate a new arti%le 34T on /blog/?a%ticle id9 should display the %ontent

of the arti%le.

11

5irst we de%lare another model ob6e%t. Append the followin" %ontent to con'ig/&odels:
1%ticle title Text content +t&l de%iving

As +t&l is not an instan%e of 0ead0 / o= and 4q0 we had to add the de%iving line. If you for"et it0 there will be an error. After the route and the model0 we write the handler. 5irst0 de%lare a new Handler module. Add i&po%t +andle%.Glog inside 1pplication. s and add it into yosog.cabal. Bets write the %ontent of +andle%/Glog. s. )e start by de%larin" the module and by importin" some blo%k ne%essary to handle Html in forms.
&odule +andle%.Glog F getGlog0 K postGlog0 K get1%ticle0 H = e%e i&po%t *&po%t -- to use +t&l into 'o%&s i&po%t Yesod.2o%&.6ic FYesod6icK nic+t&l2ieldH instance Yesod6ic 1pp ,emark: it is a best pra%ti%e to add the Yesod>i% instan%e inside 2oundation. s. I put this definition here to make thin"s easier but you should see a warnin" about this orphan instan%e. 8o put the in%lude inside 5oundation.hs is left as an e1er%i%e to the reader. Hint: Do not forget to put YesodNic and nicHtmlField inside the exported objects of the module. ent%y2o%& !! 2o%& 1%ticle ent%y2o%& : %ende%Divs < 1%ticle ?<9 a%eq text2ield )Title) 6ot ing ?*9 a%eq nic+t&l2ield )Content) 6ot ing

8his fun%tion defines a form for addin" a new arti%le. ;ont pay attention to all the synta1. If you are %urious you %an take a look at Appli%ati/e 5un%tor. You 6ust ha/e to remember a%eq is for re:uired form input. Its ar"uments bein": a%eq type label de'aultXvalue.
-- T e vie= s o=ing t e list o' a%ticles getGlog0 !! +andle% 0ep+t&l getGlog0 : do -- 3et t e list o' a%ticles inside t e database. a%ticles ?- %unDG < select;ist ,- ,Desc 1%ticleTitle-- Ye8ll need t e t=o )obWects)! a%ticleYidget and enctype -- to const%uct t e 'o%& Fsee te&plates/a%ticles. a&letH. Fa%ticleYidgetK enctypeH ?- gene%ate2o%&Tost ent%y2o%& de'ault;ayout < do <F=idget2ile )a%ticles)H

8his handler should display a list of arti%les. )e "et the list from the ;B and we %onstru%t the form. Gust take a look at the %orrespondin" template: 1.

arti%les.hamlet
? .9 1%ticles <i' null a%ticles -- / o= a standa%d &essage i' t e%e is no a%ticle ?p9 T e%e a%e no a%ticles in t e blog <else -- / o= t e list o' a%ticles ?ul9 <'o%all 4ntity a%ticle*d a%ticle ?- a%ticles ?li9 ?a %e':V@1%ticle0 a%ticle*dA 9 5@a%ticleTitle a%ticleA ? %9 ?'o%& &et od:post enctype:5@enctypeA9 Z@a%ticleYidgetA ?div9 ?input type:sub&it value:)Tost 6e= 1%ticle)9

You should remark we added some lo"i% inside the template. 8here is a test and a 3loop4. Another /ery interestin" part is the %reation of the form. 8he a%ticleYidget was %reated by yesod. )e ha/e "i/en him the ri"ht parameters *input re:uired or optional0 labels0 default /alues+. And now we ha/e a prote%ted form made for us. But we ha/e to %reate the submit button. 9et ba%k to +andle%/Glog. s.
-- =e continue +andle%/Glog. s postGlog0 !! +andle% 0ep+t&l postGlog0 : do FF%esKa%ticleYidgetHKenctypeH ?- %un2o%&Tost ent%y2o%& case %es o' 2o%&/uccess a%ticle -9 do a%ticle*d ?- %unDG < inse%t a%ticle setSessage < to+t&l < Fa%ticleTitle a%ticleH ?9 ) c%eated) %edi%ect < 1%ticle0 a%ticle*d X -9 de'ault;ayout < do setTitle )Tlease co%%ect you% ent%y 'o%&) <F=idget2ile )a%ticle1dd4%%o%)H

8his fun%tion should be used to %reate a new arti%le. )e handle the form response. If there is an error we display an error pa"e. 5or e1ample if we left some re:uired /alue blank. If thin"s "oes ri"ht:

we add the new arti%le inside the ;B *%unDG < inse%t a%ticle+ we add a messa"e to be displayed *setSessage < ...+ we are redire%ted to the arti%le web pa"e.

Here is the %ontent of the error -a"e:


?'o%& &et od:post enctype:5@enctypeA9 Z@a%ticleYidgetA ?div9 ?input type:sub&it value:)Tost 6e= 1%ticle)9

5inally we need to display an arti%le:

17

get1%ticle0 !! 1%ticle*d -9 +andle% 0ep+t&l get1%ticle0 a%ticle*d : do a%ticle ?- %unDG < getR#R a%ticle*d de'ault;ayout < do setTitle < to+t&l < a%ticleTitle a%ticle <F=idget2ile )a%ticle)H

8he getR#R fun%tion try to do a "et on the ;B. If it fails it return a =0= pa"e. 8he rest should be %lear. Here is the %ontent of te&plates/a%ticle. a&let: arti%le.hamlet
? .9 5@a%ticleTitle a%ticleA ?a%ticle9 5@a%ticleContent a%ticleA

8he blo" system is finished. Gust for fun0 you %an try to %reate an arti%le with the followin" %ontent:
?p91 last t%y to ?e&9c%oss sc%ipt?/e&9 and ?e&9/[; inWection?/e&9?/p9 ?p9+e%e is t e 'i%st t%y! ?sc%ipt9ale%tF)You loose)H(?/sc%ipt9?/p9 ?p9 1nd +e%e is t e last ?/p9 )H( D0UT T1G;4 10T*C;4((

Conclusion
8his is the end of this tutorial. I made it /ery minimal. If you already know Haskell and you want to "o further0 you should take a look at the re%ent i1Fn blo" tutorial. It will be ob/ious I inspired my own tutorial on it. Youll learn in a /ery strai"htforward way how easy it is to use authori ations0 8ime and internationali ation. If0 on the other hand you dont know Haskell. 8hen you shouldnt 6ump dire%tly to web pro"rammin". Haskell is a /ery %omple1 and unusual lan"ua"e. (y ad/i%e to "o as fast as possible in usin" Haskell for web pro"rammin" is: 1. #tart by try Haskell in your browser .. 8hen read the e1%ellent Bearn you a Haskell for 9reat 9ood 7. If you ha/e diffi%ulties in understandin" %on%epts like monads0 you should really read these arti%les. 5or me they were enli"htenin". =. If you feel %onfident0 you should be able to follows the yesod book and if you find diffi%ult to follows the yesod book0 you should read real world Haskell first *it is a must read+. Also0 note that:

haskell.or" is full of e1%ellent resour%es. hoo"le will be /ery useful 'se hlint as soon as possible to "et "ood habits.

As you should see0 if you dont already know Haskell0 the path is lon" but I "uaranty you it will be /ery rewardin"? 1=

ps: You %an download the sour%e of this yesod blo" tutorial at "ithub.%om@yo"sototh@yoso".

1. 2ne %an ar"ue these ben%hmark %ontains many problems. But the ben%hmarks are 6ust here to "i/e an order of idea. (ainly Haskell is /ery fast. .. 9enerally high level Haskell is slower than !0 but low level Haskell is e:ui/alent to ! speed. It means that e/en if you %an easily link ! %ode with Haskell0 this is not needed to rea%h the same speed. 5urthermore writin" a web ser/i%e in !@!II seems to be a /ery bad idea. You %an take a look at a dis%ussion on H> about this. 7. If you are %urious0 you %an sear%h about the 5ibona%%i node.6s troll. )ithout any tweakin"0 Haskell handled this problem perfe%tly. I tested it myself usin" yesod instead of #nap. =. By /iew I mean yesod wid"ets hamlet0 lu%ius and 6ulius files.

1<

Potrebbero piacerti anche