98

From Simple IO to Monad Transformers

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: From Simple IO to Monad Transformers
Page 2: From Simple IO to Monad Transformers

TitlePageCopyrightPageAnnotatedListofKeywordsAnnotatedListofSectionsPrefaceI.PureVersusImpureIaScopeExampleIbImpureCode

II.FunctionalProgrammingandtheRealWorldIIaDecomposedHaskellProgramIIbQuestionIIcAnswer

III.Monadsandthe(>>=)CombinatorIIIaQuestionIIIbAnswer

IV.ThereturnFunctionIVagetIntIVbaskIVcreturn_or_recursIVdQuestionIVeAnswer

V.MimickingCompositionofFunctionsVaQuestionVbAnswer

VI.ThedoBlockVIaScopingIndoBlockVIbJava-likeScopingVIcEquivalentMonadicExpressionVIdSubstitution1VIeSubstitution2VIfSubstitution3VIgMonadicExpressionEquivalenttodoBlockVIhEquivalenceStep1VIjEquivalenceStep2VIkEquivalenceStep3VIlEquivalenceStep4VImEquivalenceStep5VInQuestionVIoAnswer

Page 3: From Simple IO to Monad Transformers

VII.TheListMonadVIIaNesteddoBlockLoopVIIbDefiningpairsIVIIcDefiningpairsIIVIIdDefiningpairsIIIVIIeQuestionVIIfAnswerVIIgProgramTestingpairs

VIII.IntroductiontoMonadTransformersVIIIa'HelloWorld'VIIIb'HelloWorld'UsingLisTVIIIcABitAboutGHCIVIIIdGHCIExampleVIIIeNonloopExampleVIIIfQuestionVIIIgAnswer

IX.IntrototheStateMonadIXaStateMonad(>>=)IXbQuestionIXcAnswer

X.MoreAbouttheStateMonadXaDeriving>>fortheStatemonadXbTestinggetandputXcQuestionXdAnswer

XI.AStateTExample.XIaRepeatednext_coinsXIbDefinitionofrecursXIcDefinitionofdispenseXIdQuestionXIeAnswer

XII.MonadsXIIaDefinitionofMonadXIIbMonadicAxiomsXIIcQuestionXIIdAnswer

AbouttheAuthor

Page 4: From Simple IO to Monad Transformers

FromSimpleIOtoMonadTransformersbyJAdrianZimmer

Readerdiscussionencouragedatthisebooklet'swebpage.

PublishedbyBonsaiReadsanddistributedbyAmazon.

CoverDesign:JenniferMelot

Page 5: From Simple IO to Monad Transformers

Copyright2014byJAdrianZimmerAllrightsreserved.Permissiontocopyisgrantedtothepurchaserforpersonaluseprovidedpurchasertakesreasonablestepstoprohibitfurthercopyingbyothers.

Thisisversion1.2.Seefordifferencesbetweenversions.

Therearetwotablesofcontents.Oneisanannotatedlistofsections.Theotherhasalistingforeverymajorcodesegment.

Page 6: From Simple IO to Monad Transformers

AnnotatedListofKeywordsmonad

EstablishingamindsetforunderstandingHaskell'smonadsisthepurposeofthispublication.

Haskell

ReadersareexpectedtoknowhowtowritesimplerecursiveprogramsintheHaskellprogramminglanguage.

functionalprogramming

Functionalandimperativeprogrammingarecompared.

IO

SomeexamplesinvolveconsoleIOinHaskell.

listmonad

Haskell'sListmonadisdescribed.

statemonad

Haskell'sStatemonadisdescribed.

monadtransformer

Examplesofmonadtransformersaregiven.

smallscreen

Thismonographhasbeenformattedforpagingreadersonsmallorlargescreens.

Page 7: From Simple IO to Monad Transformers

Manycodeexamplesarehardcodedintotheirownpages.Haskellsourcecodehasbeenkepttoshortlinelengths.

Page 8: From Simple IO to Monad Transformers

AnnotatedListofSectionsI.PureVersusImpure

Mathematicalfunctionsversusside-effectsincomputerprograms.

II.FunctionalProgrammingandtheRealWorld

Haskell'swayofdealingwiththerealworld.

III.Monadsandthe(>>=)Combinator

AnintuitivelookathowmonadsworkusingtheIOmonad's(>>=)combinator.

IV.ThereturnFunction

Passingvaluestomonadcreatingfunctions.

V.MimickingCompositionofFunctions

Both(>>)and(>>=)mustbeassociative.

VI.ThedoBlock

Syntacticsugarwhichenablesimperativeprogramminginapurelyfunctionallanguage.

VII.TheListMonad

Makingmapmoreflexible.

VIII.IntroductiontoMonadTransformers

Awaytocombinedifferentkindsofmonads—demonstratedwiththeListTtransformer.

Page 9: From Simple IO to Monad Transformers

IX.IntrototheStateMonad

CreatingyourownIO-likemonadtooperateonaminiworldofyourchoice.

X.MoreAbouttheStateMonad

Herewediscussinteriorobjectsandstates

XI.AStateTExample.

Embeddinglogicinacoinchanger.

XII.Monads

Hereisacompletedefinitionofamonadandhowithelpsusdrawsomeconclusionsabouttheinteriorobjectsofthereturnfunction.

Page 10: From Simple IO to Monad Transformers

Preface“FromSimpleIOtoMonadTransformers”isanintroductiontoHaskellmonads.Iwroteitforyouifyou

areawarethatfunctioncompositionmakesupsomekindofalgebraicsystem,

knowsomeHaskell,

haveseenadescriptionorexampleofHaskell'smonads,and

arenotyetcomfortablewiththeconcept.

IfyouneedsomehelpwithHaskellbeforereadingthisthenturntothefirstfivechaptersof“LearnYouaHaskellfortheGreatGood!”orreadin“HaskellHardandFast”throughsection3.2.2.

Mygoalistohelpyoudevelopanintuitivenotionofmonadthatisaccurateenoughtobeuseful.Suchintuitioncanbedifficulttoachievebecausetheconceptisabstractandexplanationsareoftenaimedatthemathematicallysophisticated.Whentheyarenot,theytendtobeofthe“Seehowtodothis!Wasn'tthateasy?”variety.

Neitherapproachworksverywell.Whatyouneedishelpunderstandinghowabstractionsorganizedetails.Contrarytopopularopinion,wecannotcreateabstractionsbyignoringdetails.Whatabstractionsdoiscontrolthetimeandplaceforsuchthinking.EdsgerDijkstraoncereactedtothetop-downprogrammingmovement–whichhehelpedstart–bywritingthathehimselfdidnotcreateprogramspurelyfromthetopdown.Hemerelyorganizedthemthatwayforclarityandcorrectness.

HereIdiscussmonadsbylookingatthemfromboththeoutsideinandtheinsideout.Thefocusissolelyonmonads.Whileitistruethattheconceptsoffunctorandapplicativetypecanbesteppingstonesonthewaytomonads,Ihaveomittedthoseconceptsintheinterestofbrevity.Onceyouhaveunderstoodmonadsitshouldnotbedifficulttofillinsimplerconcepts.

Page 11: From Simple IO to Monad Transformers

IntheinterestofprioritizingclearerexplanationsovercontentIhavealsoomittedadescriptionofalmostallofHaskell'smonads.Youwillfindplentyofdocumentationelsewhere.ThemonadsIhavechosentopresentwillgiveyouamentalmodelformakingsenseofthatdocumentation.Youcouldsaymysolegoalistolubricateyourmindforthinkingaboutthesethings.

Mostsectionshaveoneworkedexercise.ItisOKifyoucannotworkit.Justpausingtothinkaboutitwillgiveyouabetterunderstandingthanmerelyreadingthroughanexamplewould.

Aboutversion1.2:

Unlikeversions1.0and1.1whicharealmostidentical,version1.2hasarewriteofthematerialontheListandStatemonadsaswellastheListTandStateTmonadtransformers.AadditionalsectionontheStatemonadhasbeenadded.Somesmallerrorsinthesesectionshavebeencorrected.Ifyouhaveaversionearlierthan1.2andwanttoseealistofthecorrectionsmadein1.2,lookhere.

JAdrianZimmer

Page 12: From Simple IO to Monad Transformers

I.PureVersusImpureApurelyfunctionalprogramcanbeseenasamathematicalfunction.Thedefinitionofthisfunctionexplainshowtoevaluateoneexpression.Theexplanationmayinvolvetheevaluationofotherfunctions.Eachexplanationhasreferentialtransparencywhichmeansthatifyouseethesameexpressiontwicewithinonecontextthethingitrepresentswillbethesame.

Thiscontrastswiththemorecommonimperativeprogrammingparadigm.Imperativeprogramsareasequenceofcommands.Someofthesecommandschangethevaluesassignedtovariablesandperformothersideeffectssothatreferentialtransparencyislacking.

Asanexampleofreferentialtransparencyconsiderthisdefinitionofafactorialfunction

f0=1

fx=x*(f(x-1))

Toevaluatef3,xhasthefixedmeaningof3.Itdoesn'talterthisvalueduringtheevaluationoff3.Insteadtimeoutistakentoevaluatef2.Duringthatevaluationxhasafixedmeaningof2.Eachevaluationoffisaseparatecontext.

Page 13: From Simple IO to Monad Transformers

Considerthiscodewhichsetsupacontextinwhichx,y,andfaredefinedandinwhichf3istobeevaluated.

letx=1

y=2

f0=x

fx=(x+y)*(f(x-1))

inf3

Inthepartwherethedefinitionsofx,y,andfresidethereisanothercontextcontainingtheactualdefinitionofffornonzeroarguments.Thexinthedefinitionoffisnotthesameasthexwhichisboundto1.Beingaparameterthisxonlyhasavaluewhenaparticularfxisbeingevaluated.

Iassumereadersknowthattheyinthisdefinitionoffrepresents2.Thisisnormalstaticscopingofprogramminglanguages.Youwillsometimeshearthatyisfreeinthisdefinitionoff.Thisisbecauseasfarasthedefinitionoffisconcernedycanbeanyvalueforwhichtheplusoperatorworks.Howeverthisparticularcontextiscontainedinanoutercontextwhereyisboundto2.Sinceyisnotaparameterordefinedinaletexpressionitkeepsthebindingfromtheoutercontext.

Page 14: From Simple IO to Monad Transformers

ConsiderthisexamplewhichsupposesafunctionreadALinethatreturnsthenextlineofinputenteredintotheconsole.

x=readALine

printx

y=readALine

printyIftherewerereferentialtransparencywecouldconcludethat

x

equalsy.Thatisn'tnecessarilytruebecausethereisaside

effecttoevaluatingreadALine.Somethingchangessothata

differentlineisreadeachtimereadALineisevaluated.In

thelanguageoffunctionalprogrammingreadALineiscalled

“impure”.

Inallthreeexampleswehavetothinkwhatvaluesoursymbolsand

expressionsrepresent.Intheexampleswithf,thereare

contextualrulestohelpus.Theserulesdonotvaryfromprogram

toprogram.Afterwebecomecomfortablewiththem,wedon'thaveto

thinkaboutwhichvaluesofxappearwhere.Theexamplewith

readALineisdifferent.Insuchexampleswemustalwaysthink

abouthowvalueschangeascontrolflowsaroundoursourcecode.

Nothavingtothinkconsciouslyabouthowvalueschangeisthe

reasonreferentialtransparencymakesacomputerprogrameasierto

read.Butthereisapricebecausereferentialtransparencyalso

makesaprogrammoredifficulttoapplytotherealworld.The

difficultyinaccommodatingareadALinefunctionisanexample

ofthis.

Thusfunctionalprogrammingprovidesuswithadilemma.Ononehand

welikethesimplicityandclarityofapurelyfunctionalprogram.

Ontheotherwedonotlikethecomplicationsthatarisewhenwe

wanttodoIOorplaywithothersideeffects.

ThismonographisaboutHaskell'swayofdealingwiththatdilemma.

Page 15: From Simple IO to Monad Transformers

II.FunctionalProgrammingandtheRealWorldTounderstandhowafunctionalprogramcanhandlesideeffects,thinkofasideeffectaschangingsomethingintheworldoutsideoftheprogram'sdataspace.Imaginethattheworldflickersfromonerealitytoanotherasthingschange.Aprogramwhichchangestheworldthenissomethinglikethis

my_progold_world-->new_world

Insteadofchangingtheworlditmakesanewone,flickeringfromchangetochangeontheway.

Page 16: From Simple IO to Monad Transformers

AHaskellprogramisaworldremakingfunctionwhosecomponentpartsareworldremakingfunctions.Hereisanexample.

be_nice=

putStrLn"HelloWorld"

say_hello=

putStrLn"Nicetomeetyou!"

main=say_hello>>be_niceThefunctionsbe_nice,say_hello,andmaincanall

beseenashavingthistypeWorld->WorldThefunction(>>)combines

say_helloandbe_niceto

createmain.Wecanviewitstypesignatureas(>>)::

(World->World)->

(World->World)->

(World->World)Thinkof(>>)as“then”becauseweare

talkingabout

oneworldchangingactionafteranother.Leavingaside,for

themoment,preciselyhow(>>)works,wecanunderstand

howtogetsequentialitywithinafunctionalparadigmbyimagining

thatourprogramhasbeendefinedthiswaybe_niceworld=

(putStrLn"HelloWorld")world

say_helloworld=

(putStrLn"Nicetomeetyou!")world

mainworld=say_hello(be_niceworld)Onlywecannotwriteitthatway

becauseHaskell

abstractstheconceptofworldawayfromtheprogrammer.

AHaskellprogrammerismoreconcernedwithcreatingaprogramthan

withremakingworlds.Infact,theworldisfullofthingsthe

Haskellprogrammeroughtnotbeplayingwith.Soitisbetter

tomake(>>)applytosomesimpledatatypethantomakeit

refertoworldremakingfunctions.Haskellprovidesaparameterized

type,IOaforthispurpose.AsanexamplesputStrLn"hi"::IO()

putStrLn::String->IO()Thetypefor(>>)is(>>)::IOa->IOb-

>IObThisismuchsimplerthantheversionabovewhichusesworld

remaking

functions.MoreoverthisisHaskell.

YoumayhavenoticedthatIOneedsaparameterbeforeit

becomesarealtype.Suchathingisusuallycalleda

“parameterized”type.Iprefertothinkofitasametatype.I

willdiscussthepurposeofthisparameterinSectionIII.

Page 17: From Simple IO to Monad Transformers

AnotherwaytothinkofIOisthatitisatype

constructor.Giveitanargumentanditconstructsatype.

Insections

SectionVIIIand

SectionIXyouwillseeauseformonadtypeconstructorsthattake

morethanoneparameter.

TheIOmetatypeisknownasamonad.Theconceptofmonadisuseful

becauseit

emphasizescertainsimilaritiesbetweenotherwisedifferentpartsof

ourprograms.Thesesimilaritiesgiveusaframeworkinwhichto

understandnewthings.Onceyougetusedtotheframework,ithelps

withtheunderstanding.

Onewaytothinkofamonadmistoseeitasaworkshopin

whichwecancreateandworkwithobjectsoftypema.

Allmonadicworkshopsmhavea(>>)function.Its

generaltypeis

(>>)::Monadm=>

ma->mb->mbanditisoftencalleda“combinator”.Thetypesignature

for(>>)particularizesto(>>)::IOa->IOb->IObintheIOmonad.

Iusetheterm“object”foranythingaHaskellprogramcan

manipulate.Thisincludesfunctions.Objectswhosetypeisof

theformmawheremisamonadIwillcall“monadic

objects”.

Althoughthesyntaxrequirementsfor(>>)arethesameinallworkshops,the

behaviorof(>>)differs.Onewaytothinkofthiswouldbe

toimaginethateachworkshopexistsinitsownuniversewithits

ownphysicallaws.Thisofcoursewouldimplythatthe(>>)

youseeinoneworkshopwouldn'tnecessarilybehavethesameasin

another.Itdoesnotimplythatthephysicallawswithinanyone

workshoparecompletelyarbitrary.Asyoureadon,youwillsee

thatthesimilaritiesbetweenonemonadandanothergodeeper

thanmeresyntax.

Eachworkshophasitsowntoolsformanipulatingitsobjects.The

(>>)combinatorisonlyoneoffourthatmustbepresentin

allworkshops.

Page 18: From Simple IO to Monad Transformers

QuestionWhatwillthefollowingprogramdo?

main=

say_hello>>be_nice>>main

Page 19: From Simple IO to Monad Transformers

AnswerThefunctionmainwillwritethefollowingontheconsole

HelloWorld

Nicetomeetyou!

HelloWorld

Nicetomeetyou!

...

andsoonuntiltheuserstopsitwithctl-c.

Page 20: From Simple IO to Monad Transformers

III.Monadsandthe(>>=)CombinatorWehaveseentwofunctions,putStrLnand(>>),belongingtotheIOmonad.Ofthese,only(>>)isrequiredofallmonads.BothputStrLnanditscoworkergetLinehelpgivetheIOmonaditsuniquecapabilities.ThissectionintroducesasecondfunctionrequiredofallmonadsandalsoprovidesaratherconcreteexplanationoftheparameterainIOa.

Supposewewanttoechouserinput.WewouldwanttousethegetLinefunctionwhichobtainsalineofuserinputfromtheconsole.ButwecannotwriteputStrLngetLinebecausethetypesignaturesdon'tmatch;getLineproducesanIOStringandputStrLnrequiresaString.Thetypesignaturesdon'tmatchbecauseofHaskell'swayofdealingwiththepotentialimpurityofgetLine.HaskellhasgetLinereturnamonadicobjectinsteadofastring.

BecausegetLinerepresentsamonadicobjectitisopaque.Thatmeansthestringitobtainscannotbeseen.ThepotentialimpurityofgetLineissquashed.ThestringthatgetLineobtainsisstilltherebutunavailableinanycontextthatwouldviolatereferentialtransparency.Saidbluntly,codethatinvokesgetLinecannottellthedifferencebetweendifferentevaluationsofgetLine.

Thissituationissomethinglikeridingabicycleandhavingacarturnleftimmediatelyinfrontofyou.Yourimmediateconcernisavoidingthecar.Ifthecarhitsyou,thepolicewillbeconcernedwithfindingthedriver.Whenyoudealwithamonadicobject,itissimilarlyopaque.Buttherearewaysits“door”canbeopenedandthenyouaredealingwithsomethingontheinteriorratherthantheobjectasawhole.

IwillcallthestringobtainedbyanexecutionofgetLineaninteriorobject.ItisinteriortothemonadicobjectwhichgetLinereturns.

Themonadiccombinator(>>=)producesthecontextwhereaninteriorobjectcanbeviewed.Itstypesignatureis(>>=)::Monadm=>ma->(a->mb)->mbwheremisanymonadandthetypesaandbarearbitrary.

Page 21: From Simple IO to Monad Transformers

Anapplicationof(>>=)wouldlooklikemon>>=fwheremonisoftypemaandfisoftypea->mb.Itisworthemphasizingthataandbcan,butneednot,bedifferenttypes.

Itisf'sjobtomakeanobjectmon2outofoneofmon'sinteriorobjects.Itis(>>=)'sjobtodecidewhattodowiththosemon2s.

ReturningtotheIOmonadconsidermain=getLine>>=putStrLnHereisanexplanationofhowmainisexecuted.

TheruntimesystemexecutesgetLineonaninitialworld_1.DuringthisexecutiongetLinefetchesaninputlinesfromtheconsole.Itreturnsaworld_2whoseinteriorvalueiss.

TheruntimesystemevaluatesputStrLnonsproducingaworldremakingfunction.

Finallytheruntimesystemexecutestheworldremakingfunctionproducedinstep2ontheworldcreatedinstep1.Thiscreatesaworldinwhichthestringshasbeenwrittenontheconsolescreen.

YouwillbenefitbycomparingthisdescriptionwithasimilaronefortheStatemonadwhenyougetaroundtoreadingSectionIX.

Amajordifferencebetween(>>=)and(>>)isthat(>>=)canmakeuseofinteriorobjectsand(>>)cannot.That(>>)willignoreanyargumentgiventoitisexpressedinthisdefinition.

(>>)mon1mon2=mon1>>=\_->mon2

Thisisthenormalwaytodefine(>>)andwewillassumeitfromnowon.

Because(>>)isdefinedintermsof(>>=),(>>=)isthemoreimportantcombinatortotalkabout.

Page 22: From Simple IO to Monad Transformers

QuestionIftheusertypes

First

Second

whatwillthefollowingprogramwrite?

line=

(\ln->

(getLine>>=putStrLn)>>

putStrLnln)

main=

putStrLn"Gimme2lines">>

(getLine>>=line)

Page 23: From Simple IO to Monad Transformers

AnswerOfcourselinecouldhavebeendefinedthiswaylineln=(getLine>>=putStrLn)>>putStrLnlnItgetsastringandsavesthatstringuntilitdoesthe

getLine>>=putStrLn

thing.Thenitwritesthestring,sotheresultofexecutingmainis:GimmeTwoLinesFirstSecondSecondFirstHereissourcecodeyoucancompileandrunmain=getLine>>=putStrLn

Page 24: From Simple IO to Monad Transformers

IV.ThereturnFunctionLet'sturntoasomewhatmorecomplicatedexamplebydefiningafunctiongetIntwhichinsiststhattheuserinputanonnegativeinteger.SuchafunctionwillbelikegetLineinthatitwillcreateamonadicobjectwithasingleinteriorobject.OnlyinthiscasethemonadicobjectwillbeanIOIntandtheinteriorobjectwillbepreparedbyus.Haskell'sreturnfunctiongivesusthenecessarycapability.Itsgeneraltypesignatureis

return::Monadm=>a->ma

Allformsofreturncreateamonadicobjectwhoseinteriorobjects(ifany)areofthesametypeastheirparameters.

FortheIOmonadtheinteriorobjectofreturnispreciselytheargumentpassedtoit.

Page 25: From Simple IO to Monad Transformers

OurgetIntfunctioncanbedefinedasfollowswhereaskputsamessageontheconsoleaskingforanonnegativeintegerandreturn_or_recurseeitherreturnsanIntorrestartsgetInt.

getInt=

ask>>

getLine>>=

return_or_uecurse

Page 26: From Simple IO to Monad Transformers

Theaskfunctioniseasytowrite

ask::IO()

ask=putStrLn

"Pleaseenteranonnegint"

Page 27: From Simple IO to Monad Transformers

AssumingabooleanvaluedfunctionisNonNegInt,whichreturnsTrueexactlywhenitisgivenanonnegativeinteger,thereturn_or_recursefunctionisalsoeasytowrite.

return_or_recurse::String->IOInt

return_or_recurses=

ifisNonNegInts

thenreturn(reads)

elsegetInt

Haskell'sreadfunctionispolymorphicandhasversionsforconvertingfromStringtovariouskindsofnumbers.HaskellknowswhichreadtousethistimebecauseitknowsthatthisreadissupposedtoproduceanInt.WithoutsomethingthatcluesHaskellinonthedesiredtypetherewouldbedifficulties.

Whereistheclue?Itliesinthereturntypeofreturn_or_recurse.ThatmakesthereturntypeofthisparticularreturnintoIOIntandsoittakesanIntargument.

ForcompletenesshereishowisNonNegIntmightbedefined

importData.Char

isNonNegInt::String->Bool

isNonNegIntx

|lengthx==0=

False

|otherwise=

letc:cs=xin

isDigitc&&

(cs==""||isNonNegIntcs)

Aboutthename“return”:

Thename“return”makessenseinrecurse_or_returnbecausegetIntwillbeinvokedbyHaskell'sruntimeIOsystemandourreturnisreturningtothatsystem.

Formostmonadshowever“return”isapoorwordtousebecausewhatisforemostinourmindsisthatreturniscreatinganewmonadicobject.

Trynottoletnamesconfuseyou.Inprogrammingasinmathematicswemustcreatenamesfornewandsometimescomplicatedconcepts.Normallanguageisinadequateforthisandourwordchoicescanbemisleading.

Page 28: From Simple IO to Monad Transformers

Themathematicianwhowrotethewellknownchildren'sbook“ThroughtheLookingGlass”wassurelythinkingofthisnamingdifficultywhenhehadHumptyDumptyjustifyhisuseoftheword“glory”toaconfusedAlicethisway:“itmeansjustwhatIchooseittomean—neithermorenorless.”

Bytheway,youmayaswellthinkofthe(>>=)combinatoras“thenwith”.

Page 29: From Simple IO to Monad Transformers

QuestionDefineafunctionfsuchthatthisprogramwillwrite“HelloWorld”.

main==f"HelloWorld">>=putStrLn

Page 30: From Simple IO to Monad Transformers

Answer

f=return

Page 31: From Simple IO to Monad Transformers

V.MimickingCompositionofFunctionsWehaveseenacloseconnectionbetweencompositionofworldremakingfunctionsandthe(>>)combinator.InHaskellwewrite

say_hello>>be_nice

insteadof

be_nice.say_hello

buttheunderlyingthoughtprocessisn'tsoverydifferent.

Functioncompositionisassociative.Thatisfortypecompatiblefunctionsf,g,andh

(f.g).h=f.(g.h)

The.operatorisenoughlikethatthe(>>)and(>>=)combinatorsthatwewouldlikethelattertobeassociativetoo.Since(>>=)isthemoreimportant,wewanttoassertsomethinglike

(mon1>>=f)>>=g==

mon1>>=(f>>=g)

tobetrue.Ofcourse,itisnotevensyntacticallycorrect:fmustbeamonadcreatingfunctionontheleftsideofthe==butamonadicobjectontherightside.

Wecanfixthisbymakingfintoamonadicobject.Allittakesistoevaluatefonanargumentx.Itiseasytofindanappropriatexbecauseforanyf

f==\x->fx

Keepingthisinmindandrealizingthatghasnothingtodowiththatxwecanwriteanassociativityaxiomfor(>>=)thisway

(mon>>=f)>>=g==

mon>>=(\x->fx>>=g)

Makinguseofthefact(>>=)bindsfromtheleftandlettingmrepresentan

Page 32: From Simple IO to Monad Transformers

arbitrarymonadicobjectratherthananarbitrarymonad,thisaxiomisusuallywritten

m>>=f>>=g==

m>>=(\x->fx>>=g)

Usingthenormaldefinitionof(>>)itispossibletoprovefromthisaxiomthat,foranythreetypecompatiblemonadicobjects,mon1,mon2,andmon3

(mon1>>mon2)>>mon3==

mon1>>(mon2>>mon3)

WewilltakethissimilaritywithfunctioncompositionabitfurtherinSectionXII.

Page 33: From Simple IO to Monad Transformers

QuestionIsitpossibletoapplyassociativitytoremovethexfromthis?

main=

getLine>>=

(\x->

(getLine>>=

putStrLn>>

returnx)

>>=putStrLn

)

Page 34: From Simple IO to Monad Transformers

AnswerNo.Yourfirstthoughtmaybetogoforsomethinglikethismain=getLine>>=(getLine>>=putStrLn)>>=putStrLnWhichiseasiertotalkaboutifwewriteitthiswayf=getLine>>=putStrLn

main=getLine>>=f>>=putStrLnThisapproachdoesnotcarrythefirstgetLine'sinteriorobjectovertothelastputStrLn.Moreoveritissyntacticallyincorrect.Tomakethisworkfwouldhavetobesomethinglikef=\x->getLine>>=putStrLn>>returnxwhichofcoursedoesnotgetridofthex.

Thiskindofsituationiswherethedoblockshines.

Page 35: From Simple IO to Monad Transformers

VI.ThedoBlockHereisanexampleofthedoblock.

main=

do

line1<-getLine

putStrLnline1

line2<-getLine

putStrLnline2

Yourvisualexpectationsarecorrect:

therearestatementsbindingvaluestovariables,

theentireblockconsistsofstatementsthatareexecutedinthetop-downorderpresented,and

variablescopingmakessense:line1andline2arenotavailableoutsidethedoblockbutareavailabletoallstatementsfollowingtheirassignments.

Majordifferenceswithanormalimperativeprogramminglanguagearethatvariablesarebound(theycannotbereassigned)andloopslooksomewhatweird.MoreaboutloopsinSectionVII.

Page 36: From Simple IO to Monad Transformers

Concerningthescopingrules,inthefollowingthevariablexisnotbeingreassigned.Insteadtherearetwovariableswiththesamename.

main=

do

x<-getLine

dox<-getLine

putStrLnx

putStrLnx

Page 37: From Simple IO to Monad Transformers

Thatdoblockfollowsanalogousscopingrulestothoseyouareusedtofromsuchthingsas

{Stringx=getLine()

{Stringx=getLine()

putStrLn(x)

}

putStrLn(x)

}

Sincedoblockshaveanequivalentmonadicexpression,therestofthissectionisdevotedtogivingyousomeunderstandingofthatequivalence.

Page 38: From Simple IO to Monad Transformers

Forexample,thismonadicexpressionisequivalenttotherecentlyshowndoblock.

main=

getLine>>=

(\x->

(getLine>>=putStrLn)

>>putStrLnx

)

Ihavecreatedthreesubstitutionrulestogiveyouanintuitiveunderstandingofwhydoblocksarejustsyntacticsugarforunderlyingexpressionsinvolving(>>)and(>>=).Myrulesaresomewhatsloppy.Weneedintuitionheremorethanprecisemath.Inparticularweneedtounderstandthatwhatlookslikevariableassignmentisreallyjustparameterpassing.

Althoughsloppy,theserulescanworkforyoualmostallthetime.Makesurethatyouarenotmixinguptwodifferentmonadswithonesubstitutionandbecarefulnottoallowasymbolthatisusedintwodifferentwaystobecomeusedinjustoneway.Finally,cooperatewiththetypechecker.

Page 39: From Simple IO to Monad Transformers

Substitution1Sequencing:Withinadoblockandformonadicobjectsmon1andmon2theseareequivalent

mon1>>mon2

mon1

mon2

Page 40: From Simple IO to Monad Transformers

Substitution2Conversionbetweenparameterpassingandvariablebinding:Withinadoblockandforamonadicobjectmonandatypecompatiblefunctionfthefollowingareequivalent

mon>>=f

mon>>=(\x->fx)

x<-mon

fx

Page 41: From Simple IO to Monad Transformers

Substitution3Establishmentofdoblock:Amonadicobjectmonisoftenequivalenttodomon.

Page 42: From Simple IO to Monad Transformers

Nowlet'susethesubstitutionrulestoshowwhythesetwoareequivalent.

main=

do

x<-getLine

dox<-getLine

putStrLnx

putStrLnxandmain=

getLine>>=

(\x->

(getLine>>=putStrLn)

>>putStrLnx

)Webeginwiththefirstformandapplythesubstitutionrules

to

converttothesecond.

Page 43: From Simple IO to Monad Transformers

Startbyapplyingrule2toget

main=

do

x<-getLine

dogetLine>>=putStrLn

putStrLnx

Page 44: From Simple IO to Monad Transformers

Nowapplyrule3toget

main=

do

x<-getLine

getLine>>=putStrLn

putStrLnx

Page 45: From Simple IO to Monad Transformers

Nowapplyrule1toget

main=

do

x<-getLine

(getLine>>=putStrLn)

>>putStrLnx

Page 46: From Simple IO to Monad Transformers

Notingthat

(getLine>>=putStrLn)>>putStrLnx

isonemonadicexpression,rule2canbeappliedagaintoget

main=

do

getLine>>=

(\x->

(getLine>>=putStrLn)

>>putStrLnx

)

Page 47: From Simple IO to Monad Transformers

Finallyrule3getsuswherewewanttobe.

main=

getLine>>=

(\x->

(getLine>>=putStrLn)

>>putStrLnx

)

Page 48: From Simple IO to Monad Transformers

QuestionUsethesubstitutionrulestoconvertthethefollowingmonadicexpressiontodoblockform

main=

(getLine>>=putStrLn)>>

(getLine>>=putStrLn)

Hint:Thisissimplerthantheexample.

Page 49: From Simple IO to Monad Transformers

AnswerIntroduceadoblockwithrule3.

main=

do

(getLine>>=putStrLn)>>

(getLine>>=putStrLn)Thenuserule1.

main=

do

(getLine>>=putStrLn)

(getLine>>=putStrLn)Finallyapplyrule2twice.

main=

doline1<-getLine

putStrLnline1

line2<-getLine

putStrLnline2

Whichistheexamplewestartedthissectionwith.

Page 50: From Simple IO to Monad Transformers

VII.TheListMonadListswiththeirhead:tailpatternmatchingarenotintrinsicallymonadic.Haskellletsusmanipulateliststhewaywewouldexpecttodoinanyfunctionallanguage.ThinkoftheListmonadasaddingalittlesomethingtothenormallistprocessingfunctions.

ThereturnmethodintheListmonadisdefinedthisway

returnx=[x]

Theinteriorobjectsofalistarejusttheelementsofthelistandreturnxhasjustoneinteriorobject,xitself.

The(>>=)combinatorisdefinedthisway

(>>=)monf=

concat(mapfmon)

Whichmeansthat

mon>>=f

isthelistformedbyapplyingftoeachofmon'sinteriorvaluesandconcatenatingthoseoftheresultinglistswhicharenonempty.

Here'sasimpleexample

[1,2]>>=(\x->[x+1])

whichproducesthisListmonad(akalist)

[2,3]

Ifyouwantedtocompileandrunthiscode,youcouldwritethissourcefile

main=

putStrLn(show([1,2]>>=(\x->[x+1])))

Listmonadsexemplifythekindoffreedomwegetwhenatypeisbothmonadicandnonmonadic.Wecanmakelistswithboththe[]constructorandwithreturn.The[x+1]abovecouldhavebeenwrittenreturn(x+1).Doingthat,applyingsubstitutionrule3,andthinkingofreturn(x+1)asafunctionofxgives

Page 51: From Simple IO to Monad Transformers

do[1,2]>>=

(\x->return(x+1))

Thensubstitutionrule2leadsustoanequivalentdoblock

dox<-[1,2]

return(x+1)

Whatisinterestingisthatthedoblocklooksalotlikealoopwhosecontrolvariablexiteratesovertheinteriorelementsofthelist[1,2].Inmonadicterms

\x->return(x+1)

isevaluatedforeachoftheinteriorobjectsin[1,2].

CreatingloopsindoblockslikethismaybethemainvalueoftheListmonad.Suchdoblockstakelists(whichistosayListmonadicobjects)andturnthemintolists.Thisiswhatthemapfunctiondoesinmanyprogramminglanguages.Thedoblockhoweverismoreflexible.

Foramoreillustrativeexample,wewillwriteafunctionpairsthatformsthecrossproductoftwolists.Forexample,wewant

pairs[1,2]['a','b']

toevaluateto

[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

Ofcourse,wearegoingtotakeadvantageoftheloop-likebehaviorof

dox<-lst

<loopbodyforxhere>

Andweneedaninnerloopaswell.

Page 52: From Simple IO to Monad Transformers

Thisdoubleloopwillproducethedesiredorderedpairs

pairslst1lst2=do

x<-lst1

do

y<-lst2

return(x,y)

Page 53: From Simple IO to Monad Transformers

Wecanusethesubstitutionrulestoproduceanicerversion.Thinkingofreturn(x,y)asafunctionofyandapplyingrule2gives

pairslst1lst2=do

x<-lst1

do

lst2>>=(\y->return(x,y))

Page 54: From Simple IO to Monad Transformers

Thenapplyingrule3gives

pairslst1lst2=do

x<-lst1

lst2>>=(\y->return(x,y))

Page 55: From Simple IO to Monad Transformers

Applyingrule2againwegetthisprettyversion

pairslst1lst2=

do

x<-lst1

y<-lst2

return(x,y)

Normally,youwouldnotapplythesubstitutionrulestogetthisfinalversion.Youwouldjustwriteit.Ihaveusedtherulesheretoemphasizethattheimperative-stylethinkingthatcomesnaturallywithdoblocksisactuallybasedonmonadicexpressions.It'sabitamazingisn'tit?

Page 56: From Simple IO to Monad Transformers

QuestionHowwillthisexecute?

pairsgetLinegetLine

Hint:Thegeneraltypesignatureofpairsis

pairs::

Monadm=>

ma->mb->m(a,b)

Itjusthappensthatinthewayyousawit,pairswasinstantiatedtothelistmonad.

Page 57: From Simple IO to Monad Transformers

AnswerSincetheargumentstopairsarebothoftypeIOString,weknowthatinthiscasethetypeofpairsinstantiatesto

pairs::

IOString->

IOString->

IO(String,String)

KnowingthattheIOmonad,nottheListmonad,isinvolvedwilltellusthatlst1andlst2havesingleinteriorobjects.Noloopingeffectwillbeapparent.

So,iftheinputlineswere“line1”and“line2”wewouldexpecttoget

("line1","line2")

Page 58: From Simple IO to Monad Transformers

Hereisaprogramyoucancompileandexecutetotestthis.InitIhaveextractedtheinteriorobjectfrompairsgetLinegetLine,convertedittoastringandprintedit.

pairs::Monadm=>mt->mu->m(t,u)

pairslst1lst2=do

x<-lst1

y<-lst2

return(x,y)

main=pairsgetLinegetLine>>=(\z->putStrLn(showz))This

program,ofcourse,silentlyasksfortwolinesofinput.Asa

furtherexercise,youmightwanttochangeitsothatitrequests

inputwitha"Input?"prefix.Thenexecutionwouldlooklike

Input?line1

Input?line2

("line1","line2")

Page 59: From Simple IO to Monad Transformers

VIII.IntroductiontoMonadTransformersHaskellcomesbundledwithlotsofmonads.Itcanbedifficulttogetthemtoplaytogether.Forexample,ifyoutrythis

main=

do

x<-[1,2]

y<-["a","b"]

putStrLn(show(x,y))

youwillgetatypeerrorbecausethefirsttwostatementsrequirethedoblocktobeworkingwithaListmonadwhereasthethirdstatementrequiresthedoblocktobeworkingwithanIOmonad.

Haskellincludes“transformer”versionsofmanymonadswhichcanbeusedtosolvesuchproblems.Atransformerversionofamonadmletsyoucreateaversionofmthatcanbeusedinconjunctionwithdifferentmonad.Atransformerisnotamonad,butaconstructorofmonads.

Yes,thisupsthelevelofabstraction.Monadsconstructtypes.Transformersconstructmonads.Butonceyouhaveacceptedthenotionofa“constructorofwhatchamacallits”,thenotionofa“constructorofwhatchamacallitconstructors”isn'tmuchofastretch.

Tobemoreexplicitatransformermtofamonadnisametatypewithtwotypeparametersmanda.Thefirstparametermmustbeamonad.Whenitissubstituted,mtmbecomesanothermonad.Whenbothparametersaresubstituted,mtmabecomesamonadicobject.Themonadicobjectwillactlikeoneofn'sobjectswithrespectto(>>=)andreturnbutcanbeusedlikethewrapperminwaysyettobespecified.

ThetransformerfortheListmonadhasametatypesomethinglikethis

typeListTma=ListT(m[a])

WhenmisgivenaspecificvalueinListma,yougetamonadwhichactslikea

Page 60: From Simple IO to Monad Transformers

Listmonadwithrespectto(>>=)andreturnbutwhoselistiswrappedinthemonadyougavetom.

ThedefinitionofListTisactuallywrittenwithanaccessorforthemonadicobjectoftypem[a].Thisisthewayitreallylooks

newtypeListTma=

ListT{runListT::m[a]}

(Herenewtypeisusedbecausetypemerelynamesanaliasforanothertypeandwecannotspecifyaccessorwhennaminganalias.)

SincetheonlyListTmIwillbewritingaboutisListTIO,itisconvenienttogiveitaname:

typeLio=ListTIO

SinceaLiomonadicobject's(>>=)andreturnactlikeaList,itsinteriorobjectscomefromthelistwhichtheIOmonadwraps.Putanotherway,iftheIO'sinteriorobjectis['one','two']thentheLio'sinteriorobjectsare'one'and'two'.

ActuallyusingtheIOmonadwithinLioisnotstraightforward.Thiswillnotwork

hello::Lio()

hello=do

putStrLn"Thisiswrong!"

TheproblemisthatputStrLnproducesanIO()andthisdoblockneedsaLio().Wesolvethatproblemby“lifting”theputStrLnactionintothecontainingLio.

Page 61: From Simple IO to Monad Transformers

Soifwewanttowrite“HelloWorld”fromwithinLio,hereisthewaytodoit.

hello::Lio()

hello=do

lift$

putStrLn"HelloWorld"

Sofarsogood,butliftingdoesnotsolvetheentireproblem.NoticethathellocannotgoatthetoplevelofaprogrambecauseitisnotanIOmonad.AwayofextractingLio'sIOmonadisneeded.

Page 62: From Simple IO to Monad Transformers

ExtractinganIOfromaLioiswhattherunListTaccessorisfor.Hereisaprogramyoucanrun

importControl.Monad.List

typeLio=ListTIO

hello::Lio()

hello=do

lift$putStrLn"HelloWorld"

main=runListThello

Ifcompiled,theoutputofthismainwillbe

HelloWorld

Page 63: From Simple IO to Monad Transformers

ThismonographisnotabouttheGHCIinterpreterbut,forthoseofyouwhoareworkingwithGHCI,hereisatranslationofthisoneprogramtoaformtheinterpreterwouldliketosee

>importControl.Monad.List

>typeLio=ListTIO

>lethello=dolift$putStrLn"HelloWorld"

>runListThelloFromthisyouwillgetHelloWorld

[()]

whichshowsboththedesiredoutputandtheinteriorobjectofthemonadthatiscreated.Theoutput,ofcourse,isasideeffecdtthattheIOmonadistryingtohide.The[()]isaninteriorobjectoftheIOmonad.You'llseeonereasonwhythatisimportantinthissection'sexercise.

WeirdlyGHCIthrowsthatinteriorobjectawayifitis()andshowsittoyouotherwise.

Bytheway:

Mostofthesourcecodeyouputintoa.hsfilecanbeexecutedinGHCI.Foranexamplesupposehello.hscontainedimportControl.Monad.List

typeLio=ListTIO

hello=dolift$putStrLn"HelloWorld"

main=runListThelloIhavepurposelyleftthetypesignatureforhellooffbecauseIwantGHCItotellmewhatitis.

Page 64: From Simple IO to Monad Transformers

HereisaGHCIsessioninwhichIcheckthetypeofhello

Prelude>:lhello.hs

[1or1]CompilingMain

Ok,modulesloaded:Main.

*Main>main

HelloWorld

[()]

*Main>:thello

hello::ListTIO()

Page 65: From Simple IO to Monad Transformers

Nowlet'sexperimentabit.Ifwerunthis

importControl.Monad.List

typeLio=ListTIO

test::Lio()

test=do

x<-return[1,2]

y<-ListT$return['a','b']

lift$putStrLn(show(x,y))

main=runListTtest

Page 66: From Simple IO to Monad Transformers

weget

([1,2],'a')

([1,2],'b')

Doesitseemstrangethatyiteratesthroughthelist['a','b']butxdoesnotiteratethroughthelist[1,2]?

Thereasonisthis:thereturnusedinthexassignmentexistswithinadowhichmustproduceanLio().ItactslikeList'sreturn.Thereforeitsinteriorobjectisthesingleelementofthelist[[1,2]].

ThereturnintheassignmenttoyisdifferentinthatitmustproducesomethingfortheListTconstructor,thatistosayitmustproduceanIOa.ThereforeitactslikeanIOreturnandsoitproducesamonadicobjectwhoseinteriorobjectis['a','b'].TheListTconstructorthenbuildstheLiomonadicobjectanditsinteriorobjectsare'a'and'b'.

Thereforexiscomingfromalistwhosesingleelementis[1,2]andyiscomingfromalistwhoseelementsare'a'an'b'.

Whew!YoureallydohavetopayattentiontoHaskell'stypesystemwhenfiguringoutwhatreturndoes.

Page 67: From Simple IO to Monad Transformers

QuestionRecallingtherecenttestexample,whathappensifyoudothis?

runListT(test>>test)Explain.

Hint:Thisoneistricky.Youwoulddowelltoreviewthepairs

example

andtheexerciseoftheprevioussection.Afterthat,thinkabout

what

theinteriorobjectoftheIOwrapperintestis.Evenif

youstillhavetoreadtheanswer,youwillbebetterpreparedto

understanditifyoudothisreviewandmakethisattempt.

Hereisagoodplacetoslowdownandmakesureyoureallygrokwhat's

goingon.Ifyoudon't,thingswillonlygetmoreconfusingasyou

goforward.

Page 68: From Simple IO to Monad Transformers

AnswerTheansweris([1,2],'a')([1,2],'b')([1,2],'a')([1,2],'b')([1,2],'a')([1,2],'b')Tounderstand,noticethatrunListTtestdoesnotonlyinvokeputStrLntwicebutalsoreturnsthislist[(),()].(IfyouareintoGHCI,youcancheckthisbyloadingtest.hssourcecodeandevaluatingmain.)Hencetesthastwointeriorobjects,both().Armedwiththisinformationyouknowthatthefirsttestwilloutput([1,2],'a')([1,2],'b')andproducetwointeriorobjects.The(>>)combinatorwillignoretheinteriorobjectsbutwillbeevaluatethesecondtesttwiceanyway.Eachofthoseevaluationswillhavethesideeffectofprintingtheabovetwolines.

Page 69: From Simple IO to Monad Transformers

IX.IntrototheStateMonadTheStatemonadisreminiscentoftheIOmonad.TheIOmonadhidesworldremakingfunctionswhereastheStatemonadwrapsstateremakingfunctions.Buthidingismorecompletethanwrapping.TheIOmonadhidesdetailsfromtheprogrammer.TheStatemonadallowstheprogrammertowrapdetailsinanabstraction.

Inbothmonadsthis

mon1>>mon2

worksbyevaluatingmon1andpassingtheremadeworldtomon2.

BothanIOaandaStateawrapafunction.Thefunctioncreatesanewworldorstateandaninteriorobjectoftypea.Haskellletsusdescribesuchathingwith

s->(a,s)

wheresisthestateandaistheinteriorobject.WiththisinmindwecandescribeaStatemonadthisway

newtypeStatesa=

State{runState::s->(a,s)}

TheStatemonadwrapsastateremakingfunctionandgivesitaname,runState.

Note:

Therearetwoparameters.Likeamonadtransformer,theso-calledStatemonadisnotamonadbutratheraconstructorofmonads.Weneedtoprovideanargumentforthestateparameter,asinStatesbeforewehaveanactualmonad.However,thiswon'tpreventusfromsaying“Statemonad”.

Evaluationof

mon>>=f

createsanewStatemonadwithanewstateremakingfunction.Thatfunctioncanlaterbeappliedtosomeinitialstate.Thisismuchlikeevaluating(>>=)intheIOmonadwhereanewworldremakingfunctioniscreatedthatisthenevaluatedby

Page 70: From Simple IO to Monad Transformers

theruntimesystemwhentheprogramisexecuted.

Page 71: From Simple IO to Monad Transformers

Hereisthedefinitionof(>>=)fortheStatemonad

(>>=)mf=

State$

\s->

leth=runStatem

(x,newSt)=hs

g=runState(fx)

in

gnewSt

Inwordshereishow(>>=)isevaluated

1. Haskellevaluatesm'sstateremakingfunctionrunStateonsomestatecreatinganewstatenewStandinteriorvaluex.

2. HaskellevaluatesfonxproducinganewStatemonadicobjectfx.Thismonadicobjectcontainsastateremakingfunctionthatwillbeimportantinthenextstep.

3. FinallyHaskellevaluatesthestateremakingfunctioncreatedinstep2onthestatecreatedinstep1.Thiscreatesanewstateandinteriorvaluewhicharereturnedasapair.

Asequenceof(>>=)operationslikethis

moninitial_state>>=

f1>>=f2…>>=fn

Page 72: From Simple IO to Monad Transformers

involvesrepeatedrecreationofa

(interiorobject,state)

pairbasedonthepreviousstateandinteriorobject.

ItwillbehelpfultolookbackatSectionIIItocontrastitwiththeway(>>=)worksintheIOmonad.ThedifferenceliesmostlyinthefactwemustdefinethedetailsofhowourStatemonadsworkwhereaswehavenoaccesstotheimplementationdetailsoftheIOmonad.

TheIOmonadhasnoanalogsfortherunStatefunctionandtheStateconstructor.IntheStatemonadtheyareinversesofeachother.WiththemyoucanmakeallkindsofextrafunctionsofakindthatwouldbeimpossiblewiththeIOmonad.YoucannotplaywiththerealworldthatisencapsulatedbytheIOmonad.

Thatsaid,itistimetoadmitthattheStatemonadisnolongerdirectlydefinedintheHaskellAPI.Haskell'sStatemonadiscurrentlydefinedintermsofitsStateTtransformer.Thedetailsdon'treallymattertotheHaskellprogrammerandIleaveadiscussionofStateTuntilSectionXII.ForpurposesofusingtheStatemonadallweneedtoknowisthatthereisnoStateconstructor.IthasbeenreplacedwithastatefunctionwhichdoestheconstructionusingaStateTmonad.

WecanviewstateasareplacementforState.Howeverthissimplesubstitutiondoesnotapplytopatternmatching.WhentherewasanormalStateconstructor,itwaspossibletowrite

Stater=stm

toextractastateremakingfunctionrfromaStatemonadicobjectstm.SincethereisnoStateconstructor,thatisnolongerpossible.Write

r=runStatestm

instead.

NeverthelessIwillspeakoftheStatemonadandusethestatefunctionasitsconstructor.

Page 73: From Simple IO to Monad Transformers

QuestionCreateasimpleStatemonadicobjectstate_obj::State[Char][Char]

whoserunStatefunctionisaconstantfunctionthatmapsanythingtothepair("one","two").

Thendefineafunctionshow_result::Showa=>State[Char]a->IO()whichwrites

First:"one"

Second:"two"

byextractingtheinteriorobjectandstatefromitsargument.YourcompleteanswerwillappearinthistemplateimportControl.Monad.State…main=show_resultstate_objNotethatshow_resultwillhavetorunstate_obj'sstateremakingfunction.Itdoesn'tmatterwhatargumentyougiveitbecauseyouaregoingtothrowtheargumentaway.Iused“anything”.

Page 74: From Simple IO to Monad Transformers

AnswerimportControl.Monad.State

state_obj::State[Char][Char]state_obj=state(\s->("one","two"))

show_result::Showa=>State[Char]a->IO()show_resultso=(putStrLn("First:"++(show(fstran))))>>(putStrLn("Second:"++(show(sndran))))whereran=runStateso"anything"

main=show_resultstate_objNotetheshow_resultfunctioncouldhavebeendeclaredshow_result::State[Char][Char]->IO()andwrittenwithout(show…).Ididnotdoitthatwaybecauselaterwewillneedshow_resulttobeflexibleenoughtoworkwhentheinteriorobjectis().

Page 75: From Simple IO to Monad Transformers

X.MoreAbouttheStateMonadLikeothermonads,theStatemonadhasareturnfunction.Itisdefinedthiswayreturn::a->Statesareturnx=state$\st->(x,st)TheaxiomsinSectionXIIprettymuchinsistthatxbetheinteriorobjectofreturnx.Let'stesttoseeifthatistrueoftheStatemonad.

Interiorobjectsarethosemadeavailablethrough>>=.Inawayyoucouldthinkofthemas“exterior”objects.Amonad'swayofexposingsomethingtotheoutsideworldis>>=andtheso-calledinteriorobjectsarewhatitexposes.

Toseewhat>>=provides,allweneedistoapplyshow_resultfromthepreviousexercisetoreturn"interior"asinmain=show_result(return"interior")ThismainwillproduceFirst:"interior"Second:"anything"

Whichshowsthattheinteriorobjectofreturn"interior"is“interior”andthestatehasremainedwhatshow_resultpassedtorunState.

WhatmakestheStatemonadinterestingisnotsomuchitsinteriorobjectbutratheritsabilitytomanipulateastateinthebackground.Theexerciseinthissectionwillinvolvedoingthatdirectlybuttherearelotsoftimes,especiallywhenusingtheStateTtransformer,youwillwanthelperfunctionsforstatemanipulation.ItishelpfultolookintothatnowbeforewegetintothecomplicationsoftheStateTmonad.

Hereisafunctionforgettingthestate.

get::Statess

get=state$\st->(st,st)Thisfunctionsimplycopiesthestateit

isgivenintotheinterior

objectposition.Thatmakesitavailablethrough>>=.

Sincegetisamonadicobjectandnotafunction,wewritem>>getnot

m>>=getBecausegetworkswithastatecomingfromrunStatem,

itmayseemstrangetouse>>.Tounderstand,let'slookat

Page 76: From Simple IO to Monad Transformers

howthestandarddefinitionof>>(>>)m1m2==m1>>=(\_->m2)mesheswiththedefinitionof>>=fortheStatemonad.

Page 77: From Simple IO to Monad Transformers

m1>>m2

==m1>>=(\_->m2)

==state$

\s->

let

(iObj,newSt)=(runStatem1)s

g=runState((\_->m2)iObj)

in

gnewSt

==state$

\s->

let

newSt=snd((runStatem1)s)

g=runStatem2

in

gnewSt

=state$

\s->

(runStatem2)(snd(runStatem1s))

Substitutingmform1andgetform2causesm>>gettoreduceto

state$

\s->(\st->(st,st))(snd(runStatems))

Page 78: From Simple IO to Monad Transformers

Inshort

m>>get

Page 79: From Simple IO to Monad Transformers

is

state$

\s->(snd(runStatems),snd(runStatems))

whichtellsusthe>>operatorworksonthestateevenasitignorestheinteriorobject.

Hereistheputfunctionforreplacingthestate.

put::s->States()

putnewState=state$\s->((),newState)

Page 80: From Simple IO to Monad Transformers

Wecantestgetandputwiththisprogram

importData.Functor.Identity

importControl.Monad.State

--show_resultandstate_objfrom

--answertoquestioninprevious

--section

main=(show_result(state_obj>>get))>>

(show_result(return"interior">>=put))

Page 81: From Simple IO to Monad Transformers

Theoutputforis

First:"two"

Second:"two"

First:()

Second:"interior"

andyoucanseethatgethasalteredtheinteriorobjectandputhasalteredboththestateandtheinteriorobject:

Youwillfindgetandputusefulbutsometimesitiseasiertowritefunctionsthataccessthestatedirectly.Thatiswhatyouwilldointhissection'sexercise.InthisexerciseweusetheStatemonadtoimplementastack.Thepushandpopfunctionscouldbewrittenwithgetandputbutitissimplerwithout.

Implementingastackinfunctionallanguagesistrivialbecauseitiseasytoaddto,orremovefrom,thefrontofalist.Sowhybotherwithamonad?Onereasonmightbetopermityourpushingandpoppingtohappeninadoblock.

InthisexercisewesetupastackthatisimplementedwithalistbutwhosepushandpopwillworkinaStatemonad'sdoblock.TheenablingfunctioninthisexampleisprocessAsStackwhichtakesalistandaStackmonadandthenusestheStackmonadtoprocessthelist.Thetypesignatureis

processAsStack::

[a]->Stacka->[a]

TheStackargumentofcoursecanbeadoblock.

HereisatypedefinitionforStack.

typeStacka=State[a]

andhereisadefinitionofprocessAsStack

processAsStack::[a]->Stacka()->[a]

processAsStacklstprocess=

snd(runStateprocesslst)

Page 82: From Simple IO to Monad Transformers

QuestionFinishthisprogramwhichcreatesastackandthenusesadoblocktoalterthelistofchars“ct”sothatitis“cat”.

Youwillneedtoimplementthepushandpopfunctions.

importData.Functor.Identity

importControl.Monad.State

typeStacka=State[a]

processAsStack::

[a]->Stacka()->[a]

processAsStacklstprocess=

snd(runStateprocesslst)

pop::Stackaa

pop=state$\(x:xs)->(x,xs)

--removesxfromthelist

push::a->Stacka()

--addstheargumenttothelist

printListlst=putStrLn(showlst)

main=

printList$

processAsStack

"ct"

$do

x<-pop

push'a'

pushx

DoyouunderstandhowHaskellknowswhatkindofthingpopis?ThedoblockispassedasthesecondargumenttoprocessAsStacksoHaskellknowsthedoblockisaStackamonadicobject.ThedoblockthereforewillhavearunStatefunctionthatmapsalistofastothekindofthingwrappedinaStackawhichisalistofas(becauseaStackaisaState[a]whichcontainsalistofas).

Sinceinitsunsugaredform,thedoblockbeginswith

pop>>=…

popitselfmustbeafunctionthattakesalistandproducesalist.

Page 83: From Simple IO to Monad Transformers

Answer

importData.Functor.Identity

importControl.Monad.State

typeStacka=State[a]

processAsStack::

[a]->Stacka()->[a]

processAsStacklstprocess=

snd(runStateprocesslst)

pop::Stackaa

pop=state$\(x:xs)->(x,xs)

push::a->Stacka()

pushx=state$\lst->((),x:lst)

printListlst=putStrLn(showlst)

main=

printList$

processAsStack

"ct"

$do

x<-pop

push'a'

pushx

Page 84: From Simple IO to Monad Transformers

XI.AStateTExample.TheStateTtransformerissimilartotheListTtransformer.Asyoumightexpect,theimportantdifferenceisthatStateTcreatesmonadsthatactlikeaStatemonadinsteadoflikeaListmonad.

WithListTmthemwrapsanentirelistbutwithStateTmthemwrapsonlythenewstate.Here'sthemetatypedefinition

newtypeStateTsma=

StateT{runStateT::s->m(a,s)}

AsexpectedofaStateTtransformerthefunctions(>>=),return,get,andputarewrittentoworklikeaStatemonad.InfacttheimplementationofStateusestheStateTmonadratherthanstartfromscratch.

Alsoasexpected,theliftfunctionpermitsfunctionsbelongingtothewrappermonadmtoworkwithinaStateTsmmonad.

Forthelastexampleinthismonographlet'stryforsomethingratherpractical.Towit,let'swriteafunctionmake_changewhichdistributeschangeforagivenamountusingcoinsofspecifieddenominations.

Ausefuldatatypeforuswillbe

typeCoins=[Int]

whereaCoinslistshowsthedenominationsofallthecoinswhichmightbeusefultous.Thealgorithmwewilladoptrequiresthatthesedenominationsbelistedbydecreasingfacevalueswith1attheveryend.Inthissimplifiedexample,weassumeaplentifulsupplyofcoinsofalldenominations.

Weassumethatwehavealreadycreatedafunctiondispensethatwillcausesomecoinmachinetodispenseacoinofaspecifieddenomination.ImplementingdispensewouldprobablyinvolvecallingaClanguagefunctionandiswaybeyondthescopeofthismonograph.Insteadwewillfakeitbyprintingoutamessageforeachcointhatisdispensed.

Beforewejumpintocodeweoughttothinkabitaboutthealgorithmwewishto

Page 85: From Simple IO to Monad Transformers

code.Itwillchooseonecoinatatimetodistributeanddecreasetheamountlefttodistributeaccordingly.ItwillbeconvenienttopullthecoinoflargestdenominationoftheCoinslistwhenitisnolongeruseful.

Sowewillbeworkingwithapairconsistingoftheamountlefttodispenseandalistofcoinsthatmightbeusefulindoingthedispensing.Sincethatlistwillendwithacoinofdenomination1,itwillalwaysbepossibletodothedispensing.Thegutsofouralgorithmwillbeafunctionnext_coinwhichdoessomethinglikethis

(amt,coins)->(amt,coins)

Eachstepthroughthesequenceeither

dispensesacoinanddecreasesamtaccordingly,or

removesthelargestcoinfromcoins.

wherethefirstalternativetakesprecedencewheneveramtisnotsmallerthanthelargestcoin.

Hereisanexampleofhowthesequencerepresentscoindispensing.Startwith(11,[10,1])whichmeanswemustmakechangefor11centswithadimeandapenny.Thefirststepisto(1,[10,1])representingthatadimehasbeendispensed.Thesecondstepisto(1,[1])representingthatwehaverecognizeddimesarenolongeruseful.Thelaststepisto(0,[1])representingthatapennyhasbeendispensed.

Page 86: From Simple IO to Monad Transformers

Now,howtoputthisalgorithmintoHaskell?Somethinglikenext_coin>>=next_coin>>=....next_coinseemsliketheformwewillwant.Let'sconsiderhowthisrepetitionofnext_coinswillendandwhetherthechoiceofhavingthestatebethecoinsratherthantheamountwascorrect.

Withinthenext_coinfunction,wemustmakeuseofboththeamountandthecoins.Thereitdoesn'tseemtomatterwhichistheinteriorobjectandwhichisthestate.Theinteriorobjectisavailableasaparameterandthestateisavailablethroughgetandput.

Outsideofnext_coin,itisadifferentstory.Thereallweneedtoworkwithistheamount.Weneedittoknowwhentostoptherepetitionofnext_coin.Thereisnoneedtoknowanythingaboutthecoinswhenimplementingourimaginedsequenceofnext_coinsabove.Bymakingtheamountlefttodistributebenext_coin'sinteriorobject,wecanplanafunctionresponsibleforrepeatingnext_coinswhichwillhaveaccesstotheamountlefttodistributethrough>>=.

Remark:

Ifyoudofuzzyreasoninglikethisandgetitwrong.Youcanalwaysbackupandtryagain.

Nowwecannaildownsomeofthetypedefinitions:SincedispenserequirestheIOmonad,wewilldefineourstatemachinetypeusingIOasthewrappermonadinStateT.Thedefinitionis

typeStMacha=StateTCoinsIOaRemark:

WhynotStateTCoinsIOInt?Forthesamereason

show_resultwasdefinedwitharatherthan[Char],

namelytheinteriorobjectwillneedtobe()atsomepoint.

Continuingwithourtypedefintions:substitutinginthe

StateTdefinitionweseethattherunStateTfunctionisrunStateT::Coins-

>IO(a,Coins)Moreoverwenowhavetypesfornext_coinanddispense

Page 87: From Simple IO to Monad Transformers

next_coin::Int->StMachInt

dispense::Int->StMach()

Page 88: From Simple IO to Monad Transformers

Sincenext_coinistoberepeateduntilitisgivenanamountof0,wecanwriterecurs::Int->StMachIntrecursamt=ifamt==0thenreturnamtelsenext_coinamt>>=recursSorecurscanbeusedthesameplacesnext_coincan.Onlyitwillrepeatnext_cointherightnumberoftimes.

Althoughwewon'tbeimplementingthedispensefunction,wewillneedastubtorunourprogram.ThebasicideaistodothisputStrLn("dispense"++(showi)++"cents")butthatwillnotworkinthecontextofaStMachmonad.WeneedtolifttothewrappingIOmonadthisway:

Page 89: From Simple IO to Monad Transformers

dispense::Int->StMach()

dispensei=

(lift$

putStrLn

"("dispense"++(showi)++cent)

)

where

cents=ifi==1

then"cent"

else"cents"

Thegutsofourcoinchangerwillbeamake_changefunctionwhichtakesalistofcoinsandanamounttobemadeintochange.

make_change::Coins->Int->IO()

Thismake_changefunctionwillworkwiththemonadicobjectrecursamtwhichmustbegiventheamountwewantdistributedandwhoserunStateTfunctionisreadytodothedistributingprovideditispassedthestartinglistofcoinswehaveavailable.Itisalmosteasytodefinemake_change

make_changecoinsamt=--notquiteright

runStateT(recursamt)coins

exceptthatthemonadicobjectproducedthiswayisanIOIntandnotanIO()asrequired.Asmalltweakfixesthat

make_changecssamt=

runStateT(recursamt)css>>return()

Allthatisleftistowritemainandnext_coin.Becausenext_coinworkswithbothamountandcoinstheimplementationwillhavetoworkwithStateT'sgetandput.RecalltheyaredefinedtoworkasifStateTwereaStatemonad.

Page 90: From Simple IO to Monad Transformers

Question

Finishthecoinchangingprogrambyfillinginthedetailsfornext_coinandmain.

importData.Functor.Identity

importControl.Monad.Trans.Class

importControl.Monad.State

typeCoins=[Int]

typeStMacha=StateTCoinsIOa

next_coin::Int->StMachInt

next_coinamt=

--finishthis

recurs::Int->StMachInt

recursamt=

ifamt==0

thenreturnamt

elsenext_coinamt>>=recurs

dispense::Int->StMach()

dispensei=

(lift$

putStrLn

("dispense"++(showi)++cents)

)

where

cents=ifi==1then"cent"else"cents"

make_change::Coins->Int->IO()

make_changecssamt=

runStateT

(recursamt)css>>return()

main=

doputStrLn"Enteranamount:"

--finishthis

Youwillneedafunctionwhichconverts[Char]stoInts.Itiscalledread.Haskellwillknowwhichreadtousebecauseyouwillputitinacontextwhereitgetsa[Char]andmustproduceanInt.

Ifyouhavetroublewiththedoblockinmain,rememberitmustbeanIOdoblock,notaStateTIOdoblock.

Page 91: From Simple IO to Monad Transformers

AnswerimportData.Functor.Identity

importControl.Monad.Trans.Class

importControl.Monad.State

typeCoins=[Int]

typeStMacha=StateTCoinsIOa

next_coin::Int->StMachInt

next_coinamt=

doc:cs<-get

ifamt>=c

then

dispensec>>

(return(amt-c))

else

putcs>>

(returnamt)

recurs::Int->StMachInt

recursamt=

ifamt==0

thenreturnamt

elsenext_coinamt>>=recurs

dispense::Int->StMach()

dispensei=

(lift$

putStrLn

("dispense"++(showi)++cents)

)

where

cents=ifi==1then"cent"else"cents"

make_change::Coins->Int->IO()

make_changecssamt=

runStateT

(recursamt)css>>return()

main=

doputStrLn"Enteranamount:"

line<-getLine

make_change

[25,10,5,1]

(readline)

Page 92: From Simple IO to Monad Transformers

XII.MonadsSofarIhaveleftoutoneoffunctionsamonadisrequiredtohaveandavoidedexplainingtwoofthethreeaxiomsthatanyimplementationshouldsatisfy.

Themissingfunctionis

fail::Monadm=>String->ma

Thisfunctionexistsindependentoftheaxiomsandisforerrorhandling.Haskellhasotherwaysofhandlingerrorsandthefailfunctionseemstobeusedverylittle.

Thetwoadditionalaxiomsensurethatreturnactssomethinglikeanalgebraicidentitywithrespecttothe(>>=)combinator.Recallthatforadditiontheidentityis0,formultiplicationitis1,andforcompositionoffunctionstheidentityis

id::a->a

idx=x

Thepatternlooksthesameforallthreeoftheseexamples.

x+0==0+x==x

x1==1x==x

f.id==id.f==f

Formonadsthepatternisabitmorecomplexbecauseunlikenormalalgebraicoperators(>>=)doesnottakethesamekindofthingontheleftandtheright.Ontheleftamonadicobjectisneededandontherightamonadmakingfunctionisneeded.Evensotherightidentityaxiomlooksprettynormal:

mon>>=return==mon

Togetreturnontheleftofthe>>=combinatorwemustmakeitintoamonadicobjectbyapplyingittosomeobjectx.Theleftidentityaxiomlookslikethis

returnx>>=f==fx

whichprettymuchsaysthatanyfwillbehavethesameifitsargumentispassedthroughreturnasitwillifitsargumentisuseddirectly.Thisisnottheusualkindofequalityweseeforidentityaxiomsbutthemeaningiseffectivelythesame.

Page 93: From Simple IO to Monad Transformers

TogetherwiththeassociativeaxiomofSectionV,therightandleftidentityaxiomsmakeuptheruleseveryimplementationofamonadmustfollow.

Page 94: From Simple IO to Monad Transformers

DefinitionofMonad

Hereisacompletedefinitionofamonadinoneplace.A“monad”misaHaskellmetatyperequiringoneargumenttobecomeanactualtype.Fourfunctionsmustbeassociatedwithm:

return::a->ma

(>>=)::ma->(a->mb)->mb

(>>)::ma->mb->mb

fail::String->ma

Page 95: From Simple IO to Monad Transformers

The(>>=)andreturnfunctionsmustsatisfythefollowingaxioms:

mon>>=f>>=g==

mon>>=(\x->fx>>=g)

returnx>>=f==fx

mon>>=return==mon

wheremon,f,andgareanyentitieswiththesetypes:

mon::ma

f::a->ma

g::a->ma

(Ascustomary,aandbrepresentarbitraryHaskelltypes.)

Inaddition(>>)isusuallydefinedwith

(>>)mon1mon2==

mon1>>=(\_->mon2)

andfailissometimesimportantforerrorreporting.

Page 96: From Simple IO to Monad Transformers

QuestionForgettingtheactualdefinitionofreturnfortheListmonad,usetheidentityaxiomstoshowthat

returnx

musthavexasitssoleinteriorobject.Inotherwords,giventhatwewanttheelementsofalisttobetheinteriorobjects,nootherdefinitionofreturnwillsatisfytheaxioms.

Hint:Myanswerinvolvestwoproofsbycontradiction.Oneshowsthatreturnxmusthaveaninteriorobjectandtheothershowsitcannothaveaninteriorobjectotherthanx.

Page 97: From Simple IO to Monad Transformers

AnswerWeshowreturnxmusthavesomeinteriorobjectbyassumingotherwiseandfindingamonforwhichthisaxiom

mon=mon>>=return

iscontradicted.

Letxbeanarbitraryvalue,thenamonadicobjectthatworksforusis

mon=[x]

Ourassumptionthatreturnxhasnointeriorobjecttranslatesintoassertingthatreturnxisanemptylist.Therefore

mon>>=return==

concat(mapreturn[x])==

concat[[]]==

[]

whichcannotbebecausemonis[x].Thusweknowthatreturncannotreturnanemptylistforanyxbecauseotherwisetheaxiomwouldbeviolated.

Weshowthatreturnxcannothaveaninteriorobjectotherthanxbyassumingthereisanotherinteriorobjectyandshowingthatthisaxiom

fx=returnx>>=f

iscontradictedforsomef.

Letxbeanarbitraryvalue.Afunctionthatworksforusis

fx=[x]

Thenthefirststepofevaluating

returnx>>=f

istoapplyftotheelementsofreturnxwithmap.Thiswillproducealistcontaininga[y].Applyingconcattothislistwillproduceanotherlist,againcontainingy.Howevertheaxiomsaysthissecondlistmustbeequaltofxwhichcontainsnoy.Thiscontradictionshowsthatforanyxthereisanfforwhichtheexistenceofanothervalueyamongreturnx'sinteriorvalueswouldleadtoacontradictionofamonadicaxiom.

Page 98: From Simple IO to Monad Transformers

AbouttheAuthorJAdrianZimmer

ZimmerisaPh.D.mathematicianwhoretiredknowingmoreaboutcomputersciencethanaboutmathematics.Hehastaughtatalllevelsfromhighschoolthoughgraduateschool.Hisexpertiseliesmoreinexplainingthingsthaninanyonespecialty.

Hishobbiesincludebiking,kayaking,andwatchingthekindofmoviesthatmakeonethinkaboutthevariouswaystobehuman.

(Seealsojazimmer.net.)