HomeUp
A(hopefully)painlessintroductiontomonads
version2
byDanBensen
©2006–2007DanBensenHomeAboutmeSitemap
Preface
Thisintroductionisintendedforexperiencedprogrammersbeginningtolearnfunctional
programming.AllthecodesamplesareinHaskell.Pleasenotethatthisisnotafulltutorial
1
.
It'sonlyanintroductiontogetyoustarted.
What'samonad?
Preface
What'samonad?
Whymonads?
Computationalpatterns
bind
(
>>=
)
Maybe
Why
IO
?
IO
and
main
Monadfunctions
then
(
>>
)
Monadsyntax
return
do
syntax
Globalsand
State
Furtherreading
Monadsareaclassofdatatypesusedinpurefunctionalprogramming.Monadsare
abstract
types
,whichmeansthateachmonadrepresentsawholeclassoftypes.Tomakeaspecific
type,youcombinethemonadwithanothertypethatdescribesthedatainsidethemonad,
whichactsasasortofcontainer.
Forexample,
IO
isthemonadassociatedwithinput/output,so
IOString
isthetype
ofvaluereturnedbyuserinputfunctions,and
IOInt
isthetypereturnedbyrandom
numbergenerators.Onceyou'vedefinedamonad,youcanuseitfordataofanyapplicable
type.
Thedatatypesthatcanworkwithamonadaredeterminedbyanoperatorthat'sdefinedfor
allmonads.Theoperatoriscalled
bind
,andiswrittenas
>>=
.
bind
isthemostimportant
featureofmonads.It'soverloadedwithauniquedefinitionforeachmonadthatgivesthe
monaditsowncharacteristicbehavior.
Whymonads?
Monadsservetwopurposes:
l
encapsulationofcommoncomputationalpatterns
l
isolationofcodethatinteractswiththeoutsideworld
Mostmonadsareusedtoexpressfrequentlyusedcomputationalpatterns.Thiseliminates
repetitioninaprogram'ssourcecodebyallowingcomputationallysimilaroperationstobe
consolidatedinthedefinitionof
bind
fortheappropriatemonad.
The
IO
monadisusedtohandleallinteractionswiththeprogram'sexternalenvironment
(I/O).Thisallowsotherfunctionstobecombinedwitheachothermoreeasily.
Computationalpatterns
Monadsreducetheamountofsourcecodeinaprogrambywrappingcomputationalpatterns
in
bind
.Thereareseveralstandardmonads,eachwithitsownparticularbehaviorpattern:
Therearemorestandardmonads,andyoucancreateyourown.Notethat
List
isthetype
ofplainoldlists.
Computationallysimilaroperationsthatactondifferentdatatypescanthenbeconsolidated
byputtingthedatainanappropriatemonadandusing
bind
tocombinethemonadvaluewith
afunctionthatoperatesonthedatainsideit.
bind(
>>=
)
Expressionsusing
bind
arewrittenas
x>>=f
.
x
isanexpressionthatreturnsa
monadvalueoftype
ma
,wherethevariable
m
representsamonad,and
a
represents
thetypeofvaluethemonadcontains.
f
isafunctionthattakesasingleargument
2
oftype
a
andreturnsamonadvalueoftype
mb
forsometype
b
.
bind
alsoreturnsavalueoftype
mb
.
Maybe
Maybe
isagoodexampleofconsolidatingsourcecode.Atype
Maybea
hastwo
kindsofvalue:Asinglevaluecalled
Nothing
torepresentafailedoperation,andaclass
ofvalueswrittenas
Justx
thatindicatesuccessfulcomputationofavalue
x
oftype
a
.
Ifyouneedtopassavaluethroughachainofoperationsthatdon'talwayssucceed,
nonmonadiccodetocallthemmightlooklikesomethingthis:
casetest1xin
Nothing>Nothing
Justy>casetest2yin
Nothing>Nothing
Justz>casetest3zin
...
Obviouslythere'sapatternhere:Thechainterminatesandreturns
Nothing
whenever
Nothing
isencountered,i.e.whenoneofthetestsfails.Itcontinuesonsuccess,passing
theresultofthelastoperationtothenextone.
Thedefinitionof
bind
in
Maybe
encapsulatesthispattern:
x>>=f=casexinNothing>Nothing
Justy>fy
Thisisthegenericcomputationalpatternofreturningonfailure.Themonadicversionofthe
codesnippetaboveisthenmuchmorecompact:
test1x>>=test2>>=test3>>=...
Thisisthemainpurposeofmostmonads.Thesourcecodehasbeenshrunkdowntoa
quarterofitsoriginalsize.Inlargeprogramswithmultiplelevelsofmonadicoperations,you
canconsolidatethecodeevenmore.
Why
IO
?
Theideabehindpurefunctionalprogrammingistomakefunctionsinacomputerprogramact
justlikemathematicalfunctions:Givenanyspecificsetofarguments,afunctionshouldalways
returnthesamevalue,anditshouldneverdoanythingelse.Itshouldn'treadfromorprintto
theuserinterface,anditshouldn'tuseorchangeanyglobalvariables.
Thispropertyisabighelp:
l
Unittestingismucheasier,becausethebehaviorofeachfunctionisindependentofthe
restoftheprogram.
l
Theprogrammerdoesn'thavetoworryaboutwhatorderfunctionsarecalledin,onlywhat
argumentstheyrecieve.Thisappliesevenwithmultiplecallstothesamefunction.
l
Sinceitdoesn'tmatterwhatorderfunctionsarecalledin,aslongastheyhavetheright
arguments,thecompilercanproducebetteroptimizedcode
3
,especiallyifmultiple
processorsareavailable.
Theonlyproblemwithpurefunctionsisthattheycan'texpressrealworldactions:
l
Inputfunctionsandrandomnumbergeneratorsdon'talwaysreturnthesamevaluefora
givensetofarguments.
l
Outputfunctionshaveaneffectontheoutsideworld.Thisisindependentofthefunction's
returnvalue,butdependentontheorderinwhichfunctioncallsaremade.
l
Functionsthatassignnewvaluestoglobalvariablescanaffectthebehavioroffunctions
thatusethosevariables.
IO
and
main
The
IO
monadisusedtoalleviatethisproblem.Placingavaluein
IO
labelsthevalueas
"impure".
4
Thecompilerkeepstheprocessingofallimpureoperationsseparatefromtherest
oftheprogrambyrequiringtheprogrammertohandletheminfunctionsthatoperateondata
inthe
IO
monad.Thispreservesthemathematicalpurityofotherfunctions,allowingthemto
beoptimizedbetterandcombinedmorefreely.
Since
IO
functionsinteractwiththeoutsideworld,whichcan'tbepassedtoafunctionasan
argument,purefunctionsaren'tallowedtocallthem.So
IO
functionscanonlybecalledby
other
IO
functions.Thismeanstheentrypointofeverypurefunctionalprogramhastobean
IO
function.InHaskell,thisfunctionis
main
,justlikeinC.
main
initiatesthechainof
IO
functioncalls,andpurefunctionscanbecalledfromanywhereinthischain.
Monadfunctions
Amonadfunctionissimplyafunctionthatreturnsamonadvalue.Itcanalsotakemonad
arguments.Insidethemonadfunction,
bind
allowsyoutopullvaluesoutoftheirmonadsand
safelypassthevaluestopurefunctions.Youcanthinkofthemonadfunctionasremovingthe
contentsofoneormoremonads,operatingonthecontentsfunctionally,andputtingthe
resultsbackintoamonadtobereturnedasthevalueofthefunction.
then(
>>
)
There'sanothermonadoperator,sometimescalled
then
,whichiswritten
>>
.
then
isdefined
intermsof
bind
:
x>>f=x>>=(\_>f)
Thestuffinparenthesesisanamelessfunction
5
(indicatedbythebackslash)thattakesan
unnamedargument(theunderline),asrequiredby
bind
.But
then
ignores
x
andjustcalls
f
.
then
evaluates
x
onlyforitssideeffects.Inthiscase,
f
shouldnottakeanargument
2
.
Basicsyntaxofmonadfunctions
Here'sasimple"HelloUser"program:
ModuleMainwhere
getName=putStr"Pleaseenteryourname:">>getLine
sayHelloname=putStrLn("Welcome,"++name)
main=getName>>=sayHello
Allofthesefunctionsare
IO
functions.Theonlyexceptionis
++
,whichconcatenatesbare
strings.
putStr
,
putStrLn
,and
getLine
arebuiltinfunctions.
Thefollowingisthesequenceofevents.Itdiffersfromfunctionalcodeinthattheorderof
eventsisstrictlypreserved.
1.
main
calls
bind
,whichcalls
getName
,whichcalls
then
,whichcalls
putStr
.
2.
putStr
printsitstextargumenttotheuser'sscreenandreturnsanullvalue.
3.
then
ignoresthereturnvalueof
putStr
andcalls
getLine
,whichwaitsforthe
usertoenteralineoftext,wrapsitinan
IO
monad,andreturnsthemonadvalueto
then
,whichreturnsitto
getName
,whichreturnsitto
bind
.
4.
bind
pullsthestringoutofthemonadreturnedby
getName
andpassesagreeting
containingthestringto
sayHello
,whichcalls
putStrLn
,whichprintsthe
greetingandreturnsanullvalue.
return
Ifyouwanttowrapthelogingreetinginafunctionthatreturnstheuser'sname,youneedto
useamonadfunctioncalled
return
.
return
doesn'tdoverymuch.Itjustputsa
valuebackintoamonad:
greetUser=getName>>=(ame>sayHelloname>>returnname
Notethat
return
isn'tanoperatorthatreturnsfromthecurrentfunction,asitisinmost
languages.It'sjustafunctionthat"returns"itsargumenttothecurrentmonad.
do
notation
Thebasicbindsyntaxisn'ttoobadforasmallfunctionlike
greetUser
,butforlarger
functions,it'snotalwayseasytoread.Forreallifecode,it'softenmoreconvenienttouse
do
notation.Themonadfunctionbeginswiththekeyword
do
,andcontainsactionsinoneoftwo
formats:
l
separatedbysemicolonsandsurroundedbycurlybraces,asinCorPerl.
l
indentedandseparatedbylinebreaks,asinPython.
Aleftarrow(
<
)
bind
stheidentifieronthelefttothecontentsofthemonadontheright.
greetUser=do{name<getName;sayHelloname;returnname}
or
greetUser=do
name<getName
sayHelloname
returnname
Globalvariablesand
State
The
State
monadencapsulatesshareddatathatmightotherwisebecontainedinglobal
variables.Unlike
IO
,valuesin
State
arepassedbetweenfunctionsasarguments,
keeping
State
withinthefunctionalmodel.
State
makesthisprocesseasierbyputting
manyvaluesintoasinglecontainer.
Furtherreading
TocontinuelearningaboutmonadsinHaskell,seethesesources:
l
WhatthehellareMonads?,byNoelWinstanley
l
AllAboutMonads,byJeffNewbern
l
YetAnotherHaskellTutorial,byHalDaumé
l
AGentleIntroductiontoHaskell
l
haskell.org
Pleaseletmeknowifanythinginthisintroductionwasconfusingordidn'tansweraquestion
youhave.Likewiseifyou'reanexperiencedHaskellhackerandyoufoundanymistakes.
footnotes
1
Becausethereareplentyenoughofthosealready.
2
Actually,
f
cantakealargernumberofarguments,callitn,butinthatcase,
x>>=f
willreturn
amonadfunctionofn1arguments,withthecontentsof
x
boundtothefirstargumentof
f
.
x>>f
simplyreturns
f
.
3
Thetheoryoflambdacalculusmakesitmucheasierfortheprogrammerswhowritecompilerstobuildin
optimizationofpurefunctions.Thisisaverypowerfultool,butisnotpossibleforfunctionswithsideeffects
(I/O)ormutabledata(variables).
4
Thisisthesourceofthespacesuitandnuclearwastemetaphors.
5
Anamelessfunctionisusuallycalledan"anonymous"function,a"lambda"function,orsimplya"lambda".It
maybetootrivialtobothergivingitaname,itmaybeusedonlyonce,anditmaydefinedintermsofdata
that'sdeterminedatruntime,inwhichcaseit'scalleda"closure".
Monad Characteristicbehavior
IO
sequentialexecutionofoperationsintime
Maybe
operationsthatmayfailtoreturnavalue
List
operationsthatcanreturnanynumberofvalues
State
operationsthatneedtosharedata