table of contents · 2015. 10. 22. · part 3 will start to dance the fine line between practical...

79

Upload: others

Post on 04-Feb-2021

1 views

Category:

Documents


0 download

TRANSCRIPT

  • 1. Introduction2. Chapter1:Whateverarewedoing?

    i. Introductionsii. Abriefencounter

    3. Chapter2:FirstClassFunctionsi. Aquickreviewii. Whyfavorfirstclass?

    4. Chapter3:PureHappinesswithPureFunctionsi. Ohtobepureagainii. Sideeffectsmayinclude...iii. 8thgrademathiv. Thecaseforpurityv. InSummary

    5. Chapter4:Curryingi. Can'tliveiflivin'iswithoutyouii. Morethanapun/Specialsauceiii. InSummary

    6. Chapter5:CodingbyComposingi. FunctionalHusbandryii. Pointfreeiii. Debuggingiv. CategoryTheoryv. InSummary

    7. Chapter6:ExampleApplicationi. DeclarativeCodingii. Aflickroffunctionalprogrammingiii. APrincipledRefactoriv. InSummary

    8. Chapter7:Hindley-MilnerandMei. What'syourtype?ii. Talesfromthecrypticiii. Narrowingthepossibilityiv. Freeasintheoremv. InSummary

    9. Chapter8:Tupperwarei. TheMightyContainerii. MyFirstFunctoriii. Schrödinger’sMaybeiv. PureErrorHandlingv. OldMcDonaldhadEffects…vi. AsynchronousTasksvii. ASpotofTheoryviii. InSummary

    10. Chapter9:MonadicOnionsi. PointyFunctorFactoryii. MixingMetaphorsiii. Mychainhitsmychestiv. Theoryv. InSummary

    TableofContents

    mostly-adequate-guide

    2

  • 11. Chapter10:ApplicativeFunctorsi. ApplyingApplicativesii. Shipsinbottlesiii. CoordinationMotivationiv. Bro,doyouevenlift?v. Freecanopenersvi. Lawsvii. InSummary

    mostly-adequate-guide

    3

  • Thisisabookonthefunctionalparadigmingeneral.We'llusetheworld'smostpopularfunctionalprogramminglanguage:JavaScript.Somemayfeelthisisapoorchoiceasit'sagainstthegrainofthecurrentculturewhich,atthemoment,feelspredominatelyimperative.However,IbelieveitisthebestwaytolearnFPforseveralreasons:

    Youlikelyuseiteverydayatwork.

    ThismakesitpossibletopracticeandapplyyouracquiredknowledgeeachdayonrealworldprogramsratherthanpetprojectsonnightsandweekendsinanesotericFPlanguage.

    Wedon'thavetolearneverythingupfronttostartwritingprograms.

    Inapurefunctionallanguage,youcannotlogavariableorreadaDOMnodewithoutusingmonads.Herewecancheatalittleaswelearntopurifyourcodebase.It'salsoeasiertogetstartedinthislanguagesinceit'smixedparadigmandyoucanfallbackonyourcurrentpracticeswhiletherearegapsinyourknowledge.

    Aboutthisbook

    mostly-adequate-guide

    4Introduction

  • Thelanguageisfullycapableofwritingtopnotchfunctionalcode.

    WehaveallthefeaturesweneedtomimicalanguagelikeScalaorHaskellwiththehelpofatinylibraryortwo.Object-orientedprogrammingcurrentlydominatestheindustry,butit'sclearlyawkwardinJavaScript.It'sakintocampingoffofahighwayortapdancingingaloshes.Wehavetobindallovertheplacelestthischangeoutfromunderus,wedon'thaveclasses(Yet),wehavevariousworkaroundsforthequirkybehaviorwhenthenewkeywordisforgotten,privatemembersareonlyavailableviaclosures.Toalotofus,FPfeelsmorenaturalanyways.

    Thatsaid,typedfunctionallanguageswill,withoutadoubt,bethebestplacetocodeinthestylepresentedbythisbook.JavaScriptwillbeourmeansoflearningaparadigm,whereyouapplyitisuptoyou.Luckily,theinterfacesaremathematicaland,assuch,ubiquitous.You'llfindyourselfathomewithswiftz,scalaz,haskell,purescript,andothermathematicallyinclinedenvironments.

    ReaditonlineDownloadEPUBDownloadMobi(Kindle)

    gitclonehttps://github.com/DrBoolean/mostly-adequate-guide.git

    cdmostly-adequate-guide/npminstallgitbook-cli-ggitbookinit

    brewupdatebrewcaskinstallcalibre

    gitbookmobi../functional.mobi

    SeeSUMMARY.md

    SeeCONTRIBUTING.md

    SeeTRANSLATIONS.md

    Part1isaguidetothebasics.I'mupdatingasIfinderrorssincethisistheinitialdraft.Feelfreetohelp!Part2willaddresstypeclasseslikefunctorsandmonadsallthewaythroughtotraversable.Ihopetosqueezeintransformersandapureapplication.Part3willstarttodancethefinelinebetweenpracticalprogrammingandacademicabsurdity.We'lllookatcomonads,f-algebras,freemonads,yoneda,andothercategoricalconstructs.

    Gitbook(forabetterreadingexperience)

    Doityourself

    TableofContents

    Contributing

    Translations

    Plansforthefuture

    mostly-adequate-guide

    5Introduction

    http://drboolean.gitbooks.io/mostly-adequate-guide/https://www.gitbook.com/download/epub/book/drboolean/mostly-adequate-guidehttps://www.gitbook.com/download/mobi/book/drboolean/mostly-adequate-guide

  • HiI'mProfessorFranklinRisby,pleasedtomakeyouracquaintance.We'llbespendingsometimetogetherasI'msupposedtoteachyouabitaboutfunctionalprogramming.Butenoughaboutme,whataboutyou?I'mhopingyou'refamiliarwiththeJavaScriptlanguage,haveateensybitofObject-Orientedexperience,andfancyyourselfaworkingclassprogrammer.Youdon'tneedtohaveaPh.DinEntomology,youjustneedtoknowhowtofindandkillsomebugs.

    Iwon'tassumeanypreviousfunctionalprogrammingknowledgebecausewebothknowwhathappenswhenyouassume,butIwillexpectyoutohaverunintosomeoftheunfavorablesituationsthatarisefromworkingwithmutablestate,unrestrictedsideeffects,andunprincipleddesign.Nowthatwe'vebeenproperlyintroduced,let'sgetonwithit.

    Thepurposeofthischapteristogiveyouafeelforwhatwe'reafterwhenwewritefunctionalprograms.Wemusthavesomeideaaboutwhatmakesaprogramfunctionalorwe'llfindourselvesscribblingaimlessly,avoidingobjectsatallcosts-aclumsyendeavorindeed.Weneedabullseyetohurlourcodetoward,somecelestialcompassforwhenthewatersgetrough.

    Nowtherearesomegeneralprogrammingprinciples,variousacronymiccredosthatguideusthroughthedarktunnelsofanyapplication:DRY(don'trepeatyourself),loosecouplinghighcohesion,YAGNI(yaain'tgonnaneedit),principleofleastsurprise,singleresponsibility,andsoon.

    Iwon'tbelaborlistingeachandeveryguidelineI'veheardthroughouttheyears...thepointisthattheyholdupinafunctionalsetting,thoughthey'remerelytangentialtoourgoal.WhatI'dlikeyoutogetafeelfornow,beforewegetanyfurther,isourintentionwhenwepokeandprodatthekeyboard;ourfunctionalXanadu.

    Let'sstartwithatouchofinsanity.Hereisaseagullapplication.Whenflocksconjointheybecomealargerflockandwhentheybreedtheyincreasebythenumberofseagullswithwhomthey'rebreeding.NowthisisnotintendedtobegoodObject-Orientedcode,mindyou,itisheretohighlighttheperilsofourmodern,assignmentbasedapproach.Behold:

    varFlock=function(n){this.seagulls=n;};

    Flock.prototype.conjoin=function(other){this.seagulls+=other.seagulls;returnthis;};

    Flock.prototype.breed=function(other){this.seagulls=this.seagulls*other.seagulls;returnthis;};

    varflock_a=newFlock(4);varflock_b=newFlock(2);varflock_c=newFlock(0);

    varresult=flock_a.conjoin(flock_c).breed(flock_b).conjoin(flock_a.breed(flock_b)).seagulls;//=>32

    Whoonearthwouldcraftsuchaghastlyabomination?Itisunreasonablydifficulttokeeptrackofthemutatinginternal

    Chapter1:Whateverarewedoing?

    Introductions

    Abriefencounter

    mostly-adequate-guide

    6Chapter1:Whateverarewedoing?

  • state.And,goodheavens,theanswerisevenincorrect!Itshouldhavebeen16,butflock_awounduppermanentlyalteredintheprocess.Poorflock_a.ThisisanarchyintheI.T.!Thisiswildanimalarithmetic!

    Ifyoudon'tunderstandthisprogram,it'sokay,neitherdoI.Thepointisthatstateandmutablevaluesarehardtofolloweveninsuchasmallexample.

    Let'stryagainwithamorefunctionalapproach:

    varconjoin=function(flock_x,flock_y){returnflock_x+flock_y};varbreed=function(flock_x,flock_y){returnflock_x*flock_y};

    varflock_a=4;varflock_b=2;varflock_c=0;

    varresult=conjoin(breed(flock_b,conjoin(flock_a,flock_c)),breed(flock_a,flock_b));//=>16

    Well,wegottherightanswerthistime.There'smuchlesscode.Thefunctionnestingisatadconfusing...(we'llremedythissituationinch5).It'sbetter,butlet'sdigdeeper.Therearebenefitstocallingaspadeaspade.Hadwedoneso,wemighthaveseenwe'rejustworkingwithsimpleaddition(conjoin)andmultiplication(breed).

    There'sreallynothingspecialatallaboutthesetwofunctionsotherthantheirnames.Let'srenameourcustomfunctionstorevealtheirtrueidentity.

    varadd=function(x,y){returnx+y};varmultiply=function(x,y){returnx*y};

    varflock_a=4;varflock_b=2;varflock_c=0;

    varresult=add(multiply(flock_b,add(flock_a,flock_c)),multiply(flock_a,flock_b));//=>16

    Andwiththat,wegaintheknowledgeoftheancients:

    //associativeadd(add(x,y),z)==add(x,add(y,z));

    //commutativeadd(x,y)==add(y,x);

    //identityadd(x,0)==x;

    //distributivemultiply(x,add(y,z))==add(multiply(x,y),multiply(x,z));

    Ahyes,thoseoldfaithfulmathematicalpropertiesshouldcomeinhandy.Don'tworryifyoudidn'tknowthemrightoffthetopofyourhead.Foralotofus,it'sbeenawhilesincewe'vereviewedthisinformation.Let'sseeifwecanusethesepropertiestosimplifyourlittleseagullprogram.

    //Originallineadd(multiply(flock_b,add(flock_a,flock_c)),multiply(flock_a,flock_b));

    mostly-adequate-guide

    7Chapter1:Whateverarewedoing?

  • //Applytheidentitypropertytoremovetheextraadd//(add(flock_a,flock_c)==flock_a)add(multiply(flock_b,flock_a),multiply(flock_a,flock_b));

    //Applydistributivepropertytoachieveourresultmultiply(flock_b,add(flock_a,flock_a));

    Brilliant!Wedidn'thavetowritealickofcustomcodeotherthanourcallingfunction.Weincludeaddandmultiplydefinitionshereforcompleteness,butthereisreallynoneedtowritethem-wesurelyhaveanaddandmultiplyprovidedbysomepreviouslywrittenlibrary.

    Youmaybethinking"howverystrawmanofyoutoputsuchamathyexampleupfront".Or"realprogramsarenotthissimpleandcannotbereasonedaboutinsuchaway".I'vechosenthisexamplebecausemostofusalreadyknowaboutadditionandmultiplicationsoit'seasytoseehowmathcanbeofusetoushere.

    Don'tdespair,throughoutthisbook,we'llsprinkleinsomecategorytheory,settheory,andlambdacalculustowriterealworldexamplesthatachievethesamesimplicityandresultsasourflockofseagullsexample.Youneedn'tbeamathematicianeither,itwillfeeljustlikeusinganormalframeworkorapi.

    Itmaycomeasasurprisetohearthatwecanwritefull,everydayapplicationsalongthelinesofthefunctionalanalogabove.Programsthathavesoundproperties.Programsthatareterse,yeteasytoreasonabout.Programsthatdon'treinventthewheelateveryturn.Lawlessnessisgoodifyou'reacriminal,butinthisbook,we'llwanttoacknowledgeandobeythelawsofmath.

    We'llwanttousethetheorywhereeverypiecetendstofittogethersopolitely.We'llwanttorepresentourspecificproblemintermsofgeneric,composablebitsandthenexploittheirpropertiesforourownselfishbenefit.Itwilltakeabitmoredisciplinethanthe"anythinggoes"approachofimperative(We'llgoovertheprecisedefinitionofimperativelaterinthebook,butfornowit'sanythingotherthanfunctionalprogramming)programming,butthepayoffofworkingwithinaprincipled,mathematicalframeworkwillastoundyou.

    We'veseenaflickerofourfunctionalnorthstar,butthereareafewconcreteconceptstograspbeforewecanreallybeginourjourney.

    Chapter2:FirstClassFunctions

    mostly-adequate-guide

    8Chapter1:Whateverarewedoing?

  • Whenwesayfunctionsare"firstclass",wemeantheyarejustlikeeveryoneelse...sonormalclass(coach?).Wecantreatfunctionslikeanyotherdatatypeandthereisnothingparticularlyspecialaboutthem-storetheminarrays,passthemaround,assignthemtovariables,whathaveyou.

    ThatisJavaScript101,butworthamentionasaquickcodesearchongithubwillshowthecollectiveevasion,orperhapswidespreadignoranceoftheconcept.Shallwegoforafeignedexample?Weshall.

    varhi=function(name){return"Hi"+name;};

    vargreeting=function(name){returnhi(name);};

    Here,thefunctionwrapperaroundhiingreetingiscompletelyredundant.Why?BecausefunctionsarecallableinJavaScript.Whenhihasthe()attheenditwillrunandreturnavalue.Whenitdoesnot,itsimplyreturnsthefunctionstoredinthevariable.Justtobesure,havealook-see:

    hi;//function(name){//return"Hi"+name//}

    hi("jonas");//"Hijonas"

    Sincegreetingismerelyturningaroundandcallinghiwiththeverysameargument,wecouldsimplywrite:

    vargreeting=hi;

    greeting("times");//"Hitimes"

    Inotherwords,hiisalreadyafunctionthatexpectsoneargument,whyplaceanotherfunctionarounditthatsimplycallshiwiththesamebloodyargument?Itdoesn'tmakeanydamnsense.It'slikedonningyourheaviestparkainthedeadofJulyjusttoblasttheairanddemandanicelolly.

    Itisobnoxiouslyverboseand,asithappens,badpracticetosurroundafunctionwithanotherfunctionmerelytodelayevaluation.(We'llseewhyinamoment,butithastodowithmaintenance.)

    Asolidunderstandingofthisiscriticalbeforemovingon,solet'sseeafewmorefunexamplesexcavatedfromnpmmodules.

    //ignorantvargetServerStuff=function(callback){returnajaxCall(function(json){returncallback(json);

    Chapter2:FirstClassFunctions

    Aquickreview

    mostly-adequate-guide

    9Chapter2:FirstClassFunctions

  • });};

    //enlightenedvargetServerStuff=ajaxCall;

    Theworldislitteredwithajaxcodeexactlylikethis.Hereisthereasonbothareequivalent:

    //thislinereturnajaxCall(function(json){returncallback(json);});

    //isthesameasthislinereturnajaxCall(callback);

    //sorefactorgetServerStuffvargetServerStuff=function(callback){returnajaxCall(callback);};

    //...whichisequivalenttothisvargetServerStuff=ajaxCall;//

  • Okay,let'sgetdowntothereasonstofavorfirstclassfunctions.AswesawinthegetServerStuffandBlogControllerexamples,it'seasytoaddlayersofindirectionthathavenoactualvalueandonlyincreasetheamountofcodetomaintainandsearchthrough.

    Inaddition,ifafunctionweareneedlesslywrappingdoeschange,wemustalsochangeourwrapperfunction.

    httpGet('/post/2',function(json){returnrenderPost(json);});

    IfhttpGetweretochangetosendapossibleerr,wewouldneedtogobackandchangethe"glue".

    //gobacktoeveryhttpGetcallintheapplicationandexplicitlypasserr//along.httpGet('/post/2',function(json,err){returnrenderPost(json,err);});

    Hadwewrittenitasafirstclassfunction,muchlesswouldneedtochange:

    //renderPostiscalledfromwithinhttpGetwithhowevermanyargumentsitwantshttpGet('/post/2',renderPost);

    Besidestheremovalofunnecessaryfunctions,wemustnameandreferencearguments.Namesareabitofanissue,yousee.Wehavepotentialmisnomers-especiallyasthecodebaseagesandrequirementschange.

    Havingmultiplenamesforthesameconceptisacommonsourceofconfusioninprojects.Thereisalsotheissueofgenericcode.Forinstance,thesetwofunctionsdoexactlythesamething,butonefeelsinfinitelymoregeneralandreusable:

    //specifictoourcurrentblogvarvalidArticles=function(articles){returnarticles.filter(function(article){returnarticle!==null&&article!==undefined;});};

    //vastlymorerelevantforfutureprojectsvarcompact=function(xs){returnxs.filter(function(x){returnx!==null&&x!==undefined;});};

    Bynamingthings,we'veseeminglytiedourselvestospecificdata(inthiscasearticles).Thishappensquiteabitandisasourceofmuchreinvention.

    Imustmentionthat,justlikewithObject-Orientedcode,youmustbeawareofthiscomingtobiteyouinthejugular.Ifanunderlyingfunctionusesthisandwecallitfirstclass,wearesubjecttothisleakyabstraction'swrath.

    varfs=require('fs');

    //scary

    Whyfavorfirstclass?

    mostly-adequate-guide

    11Chapter2:FirstClassFunctions

  • fs.readFile('freaky_friday.txt',Db.save);

    //lesssofs.readFile('freaky_friday.txt',Db.save.bind(Db));

    Havingbeenboundtoitself,theDbisfreetoaccessitsprototypicalgarbagecode.Iavoidusingthislikeadirtynappy.There'sreallynoneedwhenwritingfunctionalcode.However,wheninterfacingwithotherlibraries,you'llhavetoacquiescetothemadworldaroundus.

    Somewillarguethisisnecessaryforspeed.Ifyouarethemicro-optimizationsort,pleaseclosethisbook.Ifyoucannotgetyourmoneyback,perhapsyoucanexchangeitforsomethingmorefiddly.

    Andwiththat,we'rereadytomoveon.

    Chapter3:PureHappinesswithPureFunctions

    mostly-adequate-guide

    12Chapter2:FirstClassFunctions

  • Onethingweneedtogetstraightistheideaofapurefunction.

    Apurefunctionisafunctionthat,giventhesameinput,willalwaysreturnthesameoutputanddoesnothaveanyobservablesideeffect.

    Takesliceandsplice.Theyaretwofunctionsthatdotheexactsamething-inavastlydifferentway,mindyou,butthesamethingnonetheless.Wesaysliceispurebecauseitreturnsthesameoutputperinputeverytime,guaranteed.splice,however,willchewupitsarrayandspititbackoutforeverchangedwhichisanobservableeffect.

    varxs=[1,2,3,4,5];

    //purexs.slice(0,3);//=>[1,2,3]

    xs.slice(0,3);//=>[1,2,3]

    xs.slice(0,3);//=>[1,2,3]

    //impurexs.splice(0,3);//=>[1,2,3]

    xs.splice(0,3);//=>[4,5]

    xs.splice(0,3);//=>[]

    Infunctionalprogramming,wedislikeunwieldyfunctionslikesplicethatmutatedata.Thiswillneverdoaswe'restrivingforreliablefunctionsthatreturnthesameresulteverytime,notfunctionsthatleaveamessintheirwakelikesplice.

    Let'slookatanotherexample.

    //impurevarminimum=21;

    varcheckAge=function(age){returnage>=minimum;};

    //purevarcheckAge=function(age){varminimum=21;returnage>=minimum;};

    Intheimpureportion,checkAgedependsonthemutablevariableminimumtodeterminetheresult.Inotherwords,itdependsonsystemstatewhichisdisappointingbecauseitincreasesthecognitiveloadbyintroducinganexternalenvironment.

    Chapter3:PureHappinesswithPureFunctions

    Ohtobepureagain

    mostly-adequate-guide

    13Chapter3:PureHappinesswithPureFunctions

  • Itmightnotseemlikealotinthisexample,butthisrelianceuponstateisoneofthelargestcontributorstosystemcomplexity(http://www.curtclifton.net/storage/papers/MoseleyMarks06a.pdf).ThischeckAgemayreturndifferentresultsdependingonfactorsexternaltoinput,whichnotonlydisqualifiesitfrombeingpure,butalsoputsourmindsthroughtheringereachtimewe'rereasoningaboutthesoftware.

    Itspureform,ontheotherhand,iscompletelyselfsufficient.Wecanalsomakeminimumimmutable,whichpreservesthepurityasthestatewillneverchange.Todothis,wemustcreateanobjecttofreeze.

    varimmutableState=Object.freeze({minimum:21});

    Let'slookmoreatthese"sideeffects"toimproveourintuition.Sowhatisthisundoubtedlynefarioussideeffectmentionedinthedefinitionofpurefunction?We'llbereferringtoeffectasanythingthatoccursinourcomputationbesidesthecalculationofaresult.

    There'snothingintrinsicallybadabouteffectsandwe'llbeusingthemallovertheplaceinthechapterstocome.It'sthatsidepartthatbearsthenegativeconnotation.Wateraloneisnotaninherentlarvaeincubator,it'sthestagnantpartthatyieldstheswarms,andIassureyou,sideeffectsareasimilarbreedinggroundinyourownprograms.

    Asideeffectisachangeofsystemstateorobservableinteractionwiththeoutsideworldthatoccursduringthecalculationofaresult.

    Sideeffectsmayinclude,butarenotlimitedto

    changingthefilesysteminsertingarecordintoadatabasemakinganhttpcallmutationsprintingtothescreen/loggingobtaininguserinputqueryingtheDOMaccessingsystemstate

    Andthelistgoesonandon.Anyinteractionwiththeworldoutsideofafunctionisasideeffect,whichisafactthatmaypromptyoutosuspectthepracticalityofprogrammingwithoutthem.Thephilosophyoffunctionalprogrammingpostulatesthatsideeffectsareaprimarycauseofincorrectbehavior.

    Itisnotthatwe'reforbiddentousethem,ratherwewanttocontainthemandruntheminacontrolledway.We'lllearnhowtodothiswhenwegettofunctorsandmonadsinlaterchapters,butfornow,let'strytokeeptheseinsidiousfunctionsseparatefromourpureones.

    Sideeffectsdisqualifyafunctionfrombeingpureanditmakessense:purefunctions,bydefinition,mustalwaysreturnthesameoutputgiventhesameinput,whichisnotpossibletoguaranteewhendealingwithmattersoutsideourlocalfunction.

    Let'stakeacloserlookatwhyweinsistonthesameoutputperinput.Popyourcollars,we'regoingtolookatsome8thgrademath.

    Sideeffectsmayinclude...

    8thgrademath

    mostly-adequate-guide

    14Chapter3:PureHappinesswithPureFunctions

    http://www.curtclifton.net/storage/papers/MoseleyMarks06a.pdf

  • Frommathisfun.com:

    Afunctionisaspecialrelationshipbetweenvalues:Eachofitsinputvaluesgivesbackexactlyoneoutputvalue.

    Inotherwords,it'sjustarelationbetweentwovalues:theinputandtheoutput.Thougheachinputhasexactlyoneoutput,thatoutputdoesn'tnecessarilyhavetobeuniqueperinput.Belowshowsadiagramofaperfectlyvalidfunctionfromxtoy;

    (http://www.mathsisfun.com/sets/function.html)

    Tocontrast,thefollowingdiagramshowsarelationthatisnotafunctionsincetheinputvalue5pointstoseveraloutputs:

    (http://www.mathsisfun.com/sets/function.html)

    Functionscanbedescribedasasetofpairswiththeposition(input,output):[(1,2),(3,6),(5,10)](Itappearsthisfunctiondoublesitsinput).

    Orperhapsatable:

    Input Output

    1 2

    2 4

    3 6

    Orevenasagraphwithxastheinputandyastheoutput:

    mostly-adequate-guide

    15Chapter3:PureHappinesswithPureFunctions

    http://www.mathsisfun.com/sets/function.htmlhttp://www.mathsisfun.com/sets/function.html

  • There'snoneedforimplementationdetailsiftheinputdictatestheoutput.Sincefunctionsaresimplymappingsofinputtooutput,onecouldsimplyjotdownobjectliteralsandrunthemwith[]insteadof().

    vartoLowerCase={"A":"a","B":"b","C":"c","D":"d","E":"e","D":"d"};

    toLowerCase["C"];//=>"c"

    varisPrime={1:false,2:true,3:true,4:false,5:true,6:false};

    isPrime[3];//=>true

    Ofcourse,youmightwanttocalculateinsteadofhandwritingthingsout,butthisillustratesadifferentwaytothinkaboutfunctions.(Youmaybethinking"whataboutfunctionswithmultiplearguments?".Indeed,thatpresentsabitofaninconveniencewhenthinkingintermsofmathematics.Fornow,wecanbundlethemupinanarrayorjustthinkoftheargumentsobjectastheinput.Whenwelearnaboutcurrying,we'llseehowwecandirectlymodelthemathematicaldefinitionofafunction.)

    Herecomesthedramaticreveal:Purefunctionsaremathematicalfunctionsandthey'rewhatfunctionalprogrammingisallabout.Programmingwiththeselittleangelscanprovidehugebenefits.Let'slookatsomereasonswhywe'rewillingtogotogreatlengthstopreservepurity.

    Forstarters,purefunctionscanalwaysbecachedbyinput.Thisistypicallydoneusingatechniquecalledmemoization:

    varsquareNumber=memoize(function(x){returnx*x;});

    squareNumber(4);//=>16

    squareNumber(4);//returnscacheforinput4//=>16

    squareNumber(5);//=>25

    Thecaseforpurity

    Cacheable

    mostly-adequate-guide

    16Chapter3:PureHappinesswithPureFunctions

  • squareNumber(5);//returnscacheforinput5//=>25

    Hereisasimplifiedimplementation,thoughthereareplentyofmorerobustversionsavailable.

    varmemoize=function(f){varcache={};

    returnfunction(){vararg_str=JSON.stringify(arguments);cache[arg_str]=cache[arg_str]||f.apply(f,arguments);returncache[arg_str];};};

    Somethingtonoteisthatyoucantransformsomeimpurefunctionsintopureonesbydelayingevaluation:

    varpureHttpCall=memoize(function(url,params){returnfunction(){return$.getJSON(url,params);}});

    Theinterestingthinghereisthatwedon'tactuallymakethehttpcall-weinsteadreturnafunctionthatwilldosowhencalled.Thisfunctionispurebecauseitwillalwaysreturnthesameoutputgiventhesameinput:thefunctionthatwillmaketheparticularhttpcallgiventheurlandparams.

    Ourmemoizefunctionworksjustfine,thoughitdoesn'tcachetheresultsofthehttpcall,ratheritcachesthegeneratedfunction.

    Thisisnotveryusefulyet,butwe'llsoonlearnsometricksthatwillmakeitso.Thetakeawayisthatwecancacheeveryfunctionnomatterhowdestructivetheyseem.

    Purefunctionsarecompletelyselfcontained.Everythingthefunctionneedsishandedtoitonasilverplatter.Ponderthisforamoment...Howmightthisbebeneficial?Forstarters,afunction'sdependenciesareexplicitandthereforeeasiertoseeandunderstand-nofunnybusinessgoingonunderthehood.

    //impurevarsignUp=function(attrs){varuser=saveUser(attrs);welcomeUser(user);};

    //purevarsignUp=function(Db,Email,attrs){returnfunction(){varuser=saveUser(Db,attrs);welcomeUser(Email,user);};};

    Theexampleheredemonstratesthatthepurefunctionmustbehonestaboutitsdependenciesand,assuch,tellusexactlywhatit'supto.Justfromitssignature,weknowthatitwilluseaDb,Email,andattrswhichshouldbetellingtosaytheleast.

    We'lllearnhowtomakefunctionslikethispurewithoutmerelydeferringevaluation,butthepointshouldbeclearthatthepureformismuchmoreinformativethanitssneakyimpurecounterpartwhichisuptoGodknowswhat.

    Portable/Self-Documenting

    mostly-adequate-guide

    17Chapter3:PureHappinesswithPureFunctions

  • Somethingelsetonoticeisthatwe'reforcedto"inject"dependencies,orpasstheminasarguments,whichmakesourappmuchmoreflexiblebecausewe'veparameterizedourdatabaseormailclientorwhathaveyou(Don'tworry,we'llseeawaytomakethislesstediousthanitsounds).ShouldwechoosetouseadifferentDbweneedonlytocallourfunctionwithit.Shouldwefindourselveswritinganewapplicationinwhichwe'dliketoreusethisreliablefunction,wesimplygivethisfunctionwhateverDbandEmailwehaveatthetime.

    InaJavaScriptsetting,portabilitycouldmeanserializingandsendingfunctionsoverasocket.Itcouldmeanrunningallourappcodeinwebworkers.Portabilityisapowerfultrait.

    Contraryto"typical"methodsandproceduresinimperativeprogrammingrooteddeepintheirenvironmentviastate,dependencies,andavailableeffects,purefunctionscanberunanywhereourheartsdesire.

    Whenwasthelasttimeyoucopiedamethodintoanewapp?OneofmyfavoritequotescomesfromErlangcreator,JoeArmstrong:"Theproblemwithobject-orientedlanguagesisthey’vegotallthisimplicitenvironmentthattheycarryaroundwiththem.Youwantedabananabutwhatyougotwasagorillaholdingthebanana...andtheentirejungle".

    Next,wecometorealizepurefunctionsmaketestingmucheasier.Wedon'thavetomocka"real"paymentgatewayorsetupandassertthestateoftheworldaftereachtest.Wesimplygivethefunctioninputandassertoutput.

    Infact,wefindthefunctionalcommunitypioneeringnewtesttoolsthatcanblastourfunctionswithgeneratedinputandassertthatpropertiesholdontheoutput.It'sbeyondthescopeofthisbook,butIstronglyencourageyoutosearchforandtryQuickcheck-atestingtoolthatistailoredforapurelyfunctionalenvironment.

    Manybelievethebiggestwinwhenworkingwithpurefunctionsisreferentialtransparency.Aspotofcodeisreferentiallytransparentwhenitcanbesubstitutedforitsevaluatedvaluewithoutchangingthebehavioroftheprogram.

    Sincepurefunctionsalwaysreturnthesameoutputgiventhesameinput,wecanrelyonthemtoalwaysreturnthesameresultsandthuspreservereferentialtransparency.Let'sseeanexample.

    varImmutable=require("immutable");

    vardecrementHP=function(player){returnplayer.set("hp",player.get("hp")-1);};

    varisSameTeam=function(player1,player2){returnplayer1.get("team")===player2.get("team");};

    varpunch=function(player,target){if(isSameTeam(player,target)){returntarget;}else{returndecrementHP(target);}};

    varjobe=Immutable.Map({name:"Jobe",hp:20,team:"red"});varmichael=Immutable.Map({name:"Michael",hp:20,team:"green"});

    punch(jobe,michael);//=>Immutable.Map({name:"Michael",hp:19,team:"green"})

    decrementHP,isSameTeamandpunchareallpureandthereforereferentiallytransparent.Wecanuseatechniquecalledequationalreasoningwhereinonesubstitutes"equalsforequals"toreasonaboutcode.It'sabitlikemanuallyevaluating

    Testable

    Reasonable

    mostly-adequate-guide

    18Chapter3:PureHappinesswithPureFunctions

  • thecodewithouttakingintoaccountthequirksofprogrammaticevaluation.Usingreferentialtransparency,let'splaywiththiscodeabit.

    Firstwe'llinlinethefunctionisSameTeam.

    varpunch=function(player,target){if(player.get("team")===target.get("team")){returntarget;}else{returndecrementHP(target);}};

    Sinceourdataisimmutable,wecansimplyreplacetheteamswiththeiractualvalue

    varpunch=function(player,target){if("red"==="green"){returntarget;}else{returndecrementHP(target);}};

    Weseethatitisfalseinthiscasesowecanremovetheentireifbranch

    varpunch=function(player,target){returndecrementHP(target);};

    AndifweinlinedecrementHP,weseethat,inthiscase,punchbecomesacalltodecrementthehpby1.

    varpunch=function(player,target){returntarget.set("hp",target.get("hp")-1);};

    Thisabilitytoreasonaboutcodeisterrificforrefactoringandunderstandingcodeingeneral.Infact,weusedthistechniquetorefactorourflockofseagullsprogram.Weusedequationalreasoningtoharnessthepropertiesofadditionandmultiplication.Indeed,we'llbeusingthesetechniquesthroughoutthebook.

    Finally,andhere'sthecoupdegrâce,wecanrunanypurefunctioninparallelsinceitdoesnotneedaccesstosharedmemoryanditcannot,bydefinition,havearaceconditionduetosomesideeffect.

    Thisisverymuchpossibleinaserversidejsenvironmentwiththreadsaswellasinthebrowserwithwebworkersthoughcurrentcultureseemstoavoiditduetocomplexitywhendealingwithimpurefunctions.

    We'veseenwhatpurefunctionsareandwhywe,asfunctionalprogrammers,believetheyarethecat'seveningwear.Fromthispointon,we'llstrivetowriteallourfunctionsinapureway.We'llrequiresomeextratoolstohelpusdoso,butinthemeantime,we'lltrytoseparatetheimpurefunctionsfromtherestofthepurecode.

    ParallelCode

    InSummary

    mostly-adequate-guide

    19Chapter3:PureHappinesswithPureFunctions

  • Writingprogramswithpurefunctionsisatadlaboriouswithoutsomeextratoolsinourbelt.Wehavetojuggledatabypassingargumentsallovertheplace,we'reforbiddentousestate,nottomentioneffects.Howdoesonegoaboutwritingthesemasochisticprograms?Let'sacquireanewtoolcalledcurry.

    Chapter4:Currying

    mostly-adequate-guide

    20Chapter3:PureHappinesswithPureFunctions

  • MyDadonceexplainedhowtherearecertainthingsonecanlivewithoutuntiloneacquiresthem.Amicrowaveisonesuchthing.Smartphones,another.Theolderfolksamonguswillrememberafulfillinglifesansinternet.Forme,curryingisonthislist.

    Theconceptissimple:Youcancallafunctionwithfewerargumentsthanitexpects.Itreturnsafunctionthattakestheremainingarguments.

    Youcanchoosetocallitallatonceorsimplyfeedineachargumentpiecemeal.

    varadd=function(x){returnfunction(y){returnx+y;};};

    varincrement=add(1);varaddTen=add(10);

    increment(2);//3

    addTen(2);//12

    Herewe'vemadeafunctionaddthattakesoneargumentandreturnafunction.Bycallingit,thereturnedfunctionremembersthefirstargumentfromthenonviatheclosure.Callingitwithbothargumentsallatonceisabitofapain,however,sowecanuseaspecialhelperfunctioncalledcurrytomakedefiningandcallingfunctionslikethiseasier.

    Let'ssetupafewcurriedfunctionsforourenjoyment.

    varcurry=require('lodash.curry');

    varmatch=curry(function(what,str){returnstr.match(what);});

    varreplace=curry(function(what,replacement,str){returnstr.replace(what,replacement);});

    varfilter=curry(function(f,ary){returnary.filter(f);});

    varmap=curry(function(f,ary){returnary.map(f);});

    ThepatternI'vefollowedisasimple,butimportantone.I'vestrategicallypositionedthedatawe'reoperatingon(String,Array)asthelastargument.Itwillbecomeclearastowhyuponuse.

    match(/\s+/g,"helloworld");//['']

    Chapter4:Currying

    Can'tliveiflivin'iswithoutyou

    mostly-adequate-guide

    21Chapter4:Currying

  • match(/\s+/g)("helloworld");//['']

    varhasSpaces=match(/\s+/g);//function(x){returnx.match(/\s+/g)}

    hasSpaces("helloworld");//['']

    hasSpaces("spaceless");//null

    filter(hasSpaces,["tori_spelling","toriamos"]);//["toriamos"]

    varfindSpaces=filter(hasSpaces);//function(xs){returnxs.filter(function(x){returnx.match(/\s+/g)})}

    findSpaces(["tori_spelling","toriamos"]);//["toriamos"]

    varnoVowels=replace(/[aeiou]/ig);//function(replacement,x){returnx.replace(/[aeiou]/ig,replacement)}

    varcensored=noVowels("*");//function(x){returnx.replace(/[aeiou]/ig,"*")}

    censored("ChocolateRain");//'Ch*c*l*t*R**n'

    What'sdemonstratedhereistheabilityto"pre-load"afunctionwithanargumentortwoinordertoreceiveanewfunctionthatremembersthosearguments.

    Iencourageyoutonpminstalllodash,copythecodeaboveandhaveagoatitintherepl.Youcanalsodothisinabrowserwherelodashorramdaisavailable.

    Curryingisusefulformanythings.WecanmakenewfunctionsjustbygivingourbasefunctionssomeargumentsasseeninhasSpaces,findSpaces,andcensored.

    Wealsohavetheabilitytotransformanyfunctionthatworksonsingleelementsintoafunctionthatworksonarrayssimplybywrappingitwithmap:

    vargetChildren=function(x){returnx.childNodes;};

    varallTheChildren=map(getChildren);

    Givingafunctionfewerargumentsthanitexpectsistypicallycalledpartialapplication.Partiallyapplyingafunctioncanremovealotofboilerplatecode.ConsiderwhattheaboveallTheChildrenfunctionwouldbewiththeuncurriedmapfromlodash(notetheargumentsareinadifferentorder):

    varallTheChildren=function(elements){return_.map(elements,getChildren);};

    Wetypicallydon'tdefinefunctionsthatworkonarrays,becausewecanjustcallmap(getChildren)inline.Samewithsort,filter,andotherhigherorderfunctions(Higherorderfunction:Afunctionthattakesorreturnsafunction).

    Morethanapun/specialsauce

    mostly-adequate-guide

    22Chapter4:Currying

  • Whenwespokeaboutpurefunctions,wesaidtheytake1inputto1output.Curryingdoesexactlythis:eachsingleargumentreturnsanewfunctionexpectingtheremainingarguments.That,oldsport,is1inputto1output.

    Nomatteriftheoutputisanotherfunction-itqualifiesaspure.Wedoallowmorethanoneargumentatatime,butthisisseenasmerelyremovingtheextra()'sforconvenience.

    CurryingishandyandIverymuchenjoyworkingwithcurriedfunctionsonadailybasis.Itisatoolforthebeltthatmakesfunctionalprogramminglessverboseandtedious.

    Wecanmakenew,usefulfunctionsontheflysimplybypassinginafewargumentsandasabonus,we'veretainedthemathematicalfunctiondefinitiondespitemultiplearguments.

    Let'sacquireanotheressentialtoolcalledcompose.

    Chapter5:CodingbyComposing

    Aquickwordbeforewestart.We'llusealibrarycalledramdawhichcurrieseveryfunctionbydefault.Alternativelyyoumaychoosetouselodash-fpwhichdoesthesameandiswritten/maintainedbythecreatoroflodash.Bothwillworkjustfineanditisamatterofpreference.

    ramdalodash-fp

    Thereareunitteststorunagainstyourexercisesasyoucodethem,oryoucanjustcopy-pasteintoajavascriptREPLfortheearlyexercisesifyouwish.

    Answersareprovidedwiththecodeintherepositoryforthisbook

    var_=require('ramda');

    //Exercise1//==============//Refactortoremoveallargumentsbypartiallyapplyingthefunction

    varwords=function(str){return_.split('',str);};

    //Exercise1a//==============//Usemaptomakeanewwordsfnthatworksonanarrayofstrings.

    varsentences=undefined;

    //Exercise2//==============//Refactortoremoveallargumentsbypartiallyapplyingthefunctions

    varfilterQs=function(xs){return_.filter(function(x){returnmatch(/q/i,x);},xs);};

    //Exercise3//==============//Usethehelperfunction_keepHighesttorefactormaxtonotreferenceany//arguments

    Insummary

    Exercises

    mostly-adequate-guide

    23Chapter4:Currying

    http://ramdajs.comhttps://github.com/lodash/lodash-fphttps://github.com/DrBoolean/mostly-adequate-guide/tree/master/code/part1_exerciseshttps://github.com/DrBoolean/mostly-adequate-guide/tree/master/code/part1_exercises/answers

  • //LEAVEBE:var_keepHighest=function(x,y){returnx>=y?x:y;};

    //REFACTORTHISONE:varmax=function(xs){return_.reduce(function(acc,x){return_keepHighest(acc,x);},-Infinity,xs);};

    //Bonus1://============//wraparray'sslicetobefunctionalandcurried.////[1,2,3].slice(0,2)varslice=undefined;

    //Bonus2://============//useslicetodefineafunction"take"thattakesnelementsfromthebeginningofthestring.Makeitcurried////Resultfor"Something"withn=4shouldbe"Some"vartake=undefined;

    mostly-adequate-guide

    24Chapter4:Currying

  • Here'scompose:

    varcompose=function(f,g){returnfunction(x){returnf(g(x));};};

    fandgarefunctionsandxisthevaluebeing"piped"throughthem.

    Compositionfeelslikefunctionhusbandry.You,breederoffunctions,selecttwowithtraitsyou'dliketocombineandmashthemtogethertospawnabrandnewone.Usageisasfollows:

    vartoUpperCase=function(x){returnx.toUpperCase();};varexclaim=function(x){returnx+'!';};varshout=compose(exclaim,toUpperCase);

    shout("sendintheclowns");//=>"SENDINTHECLOWNS!"

    Thecompositionoftwofunctionsreturnsanewfunction.Thismakesperfectsense:composingtwounitsofsometype(inthiscasefunction)shouldyieldanewunitofthatverytype.Youdon'tplugtwolegostogetherandgetalincolnlog.Thereisatheoryhere,someunderlyinglawthatwewilldiscoverinduetime.

    Inourdefinitionofcompose,thegwillrunbeforethef,creatingarighttoleftflowofdata.Thisismuchmorereadablethannestingabunchoffunctioncalls.Withoutcompose,theabovewouldread:

    varshout=function(x){returnexclaim(toUpperCase(x));};

    Insteadofinsidetooutside,werunrighttoleft,whichIsupposeisastepintheleftdirection(boo).Let'slookatanexamplewheresequencematters:

    varhead=function(x){returnx[0];};varreverse=reduce(function(acc,x){return[x].concat(acc);},[]);varlast=compose(head,reverse);

    last(['jumpkick','roundhouse','uppercut']);//=>'uppercut'

    reversewillturnthelistaroundwhileheadgrabstheinitialitem.Thisresultsinaneffective,albeitinefficient,lastfunction.Thesequenceoffunctionsinthecompositionshouldbeapparenthere.Wecoulddefinealefttorightversion,however,wemirrorthemathematicalversionmuchmorecloselyasitstands.That'sright,compositionisstraightfromthemathbooks.Infact,perhapsit'stimetolookatapropertythatholdsforanycomposition.

    //associativity

    Chapter5:CodingbyComposing

    Functionalhusbandry

    mostly-adequate-guide

    25Chapter5:CodingbyComposing

  • varassociative=compose(f,compose(g,h))==compose(compose(f,g),h);//true

    Compositionisassociative,meaningitdoesn'tmatterhowyougrouptwoofthem.So,shouldwechoosetouppercasethestring,wecanwrite:

    compose(toUpperCase,compose(head,reverse));

    //orcompose(compose(toUpperCase,head),reverse);

    Sinceitdoesn'tmatterhowwegroupourcallstocompose,theresultwillbethesame.Thatallowsustowriteavariadiccomposeanduseitasfollows:

    //previouslywe'dhavetowritetwocomposes,butsinceit'sassociative,wecangivecomposeasmanyfn'saswelikeandletitdecidehowtogroupthem.varlastUpper=compose(toUpperCase,head,reverse);

    lastUpper(['jumpkick','roundhouse','uppercut']);//=>'UPPERCUT'

    varloudLastUpper=compose(exclaim,toUpperCase,head,reverse)

    loudLastUpper(['jumpkick','roundhouse','uppercut']);//=>'UPPERCUT!'

    Applyingtheassociativepropertygivesusthisflexibilityandpeaceofmindthattheresultwillbeequivalent.Theslightlymorecomplicatedvariadicdefinitionisincludedwiththesupportlibrariesforthisbookandisthenormaldefinitionyou'llfindinlibrarieslikelodash,underscore,andramda.

    Onepleasantbenefitofassociativityisthatanygroupoffunctionscanbeextractedandbundledtogetherintheirveryowncomposition.Let'splaywithrefactoringourpreviousexample:

    varloudLastUpper=compose(exclaim,toUpperCase,head,reverse);

    //orvarlast=compose(head,reverse);varloudLastUpper=compose(exclaim,toUpperCase,last);

    //orvarlast=compose(head,reverse);varangry=compose(exclaim,toUpperCase);varloudLastUpper=compose(angry,last);

    //morevariations...

    There'snorightorwronganswers-we'rejustpluggingourlegostogetherinwhateverwayweplease.Usuallyit'sbesttogroupthingsinareusablewaylikelastandangry.IffamiliarwithFowler's"Refactoring",onemightrecognizethisprocessas"extractmethod"...exceptwithoutalltheobjectstatetoworryabout.

    Pointfreestylemeansneverhavingtosayyourdata.Excuseme.Itmeansfunctionsthatnevermentionthedatauponwhichtheyoperate.Firstclassfunctions,currying,andcompositionallplaywelltogethertocreatethisstyle.

    Pointfree

    mostly-adequate-guide

    26Chapter5:CodingbyComposing

    https://lodash.com/http://underscorejs.org/http://ramdajs.com/http://martinfowler.com/books/refactoring.htmlhttp://refactoring.com/catalog/extractMethod.html

  • //notpointfreebecausewementionthedata:wordvarsnakeCase=function(word){returnword.toLowerCase().replace(/\s+/ig,'_');};

    //pointfreevarsnakeCase=compose(replace(/\s+/ig,'_'),toLowerCase);

    Seehowwepartiallyappliedreplace?Whatwe'redoingispipingourdatathrougheachfunctionof1argument.Curryingallowsustoprepareeachfunctiontojusttakeitsdata,operateonit,andpassitalong.Somethingelsetonoticeishowwedon'tneedthedatatoconstructourfunctioninthepointfreeversion,whereasinthepointfulone,wemusthaveourwordavailablebeforeanythingelse.

    Let'slookatanotherexample.

    //notpointfreebecausewementionthedata:namevarinitials=function(name){returnname.split('').map(compose(toUpperCase,head)).join('.');};

    //pointfreevarinitials=compose(join('.'),map(compose(toUpperCase,head)),split(''));

    initials("hunterstocktonthompson");//'H.S.T'

    Pointfreecodecanagain,helpusremoveneedlessnamesandkeepusconciseandgeneric.Pointfreeisagoodlitmustestforfunctionalcodeasitlet'susknowwe'vegotsmallfunctionsthattakeinputtooutput.Onecan'tcomposeawhileloop,forinstance.Bewarned,however,pointfreeisadoubleedgeswordandcansometimesobfuscateintention.NotallfunctionalcodeispointfreeandthatisO.K.We'llshootforitwherewecanandstickwithnormalfunctionsotherwise.

    Acommonmistakeistocomposesomethinglikemap,afunctionoftwoarguments,withoutfirstpartiallyapplyingit.

    //wrong-weendupgivingangryanarrayandwepartiallyappliedmapwithgodknowswhat.varlatin=compose(map,angry,reverse);

    latin(["frog","eyes"]);//error

    //right-eachfunctionexpects1argument.varlatin=compose(map(angry),reverse);

    latin(["frog","eyes"]);//["EYES!","FROG!"])

    Ifyouarehavingtroubledebuggingacomposition,wecanusethishelpful,butimpuretracefunctiontoseewhat'sgoingon.

    vartrace=curry(function(tag,x){console.log(tag,x);returnx;});

    vardasherize=compose(join('-'),toLower,split(''),replace(/\s{2,}/ig,''));

    dasherize('Theworldisavampire');

    Debugging

    mostly-adequate-guide

    27Chapter5:CodingbyComposing

  • //TypeError:Cannotreadproperty'apply'ofundefined

    Somethingiswronghere,let'strace

    vardasherize=compose(join('-'),toLower,trace("aftersplit"),split(''),replace(/\s{2,}/ig,''));//aftersplit['The','world','is','a','vampire']

    Ah!WeneedtomapthistoLowersinceit'sworkingonanarray.

    vardasherize=compose(join('-'),map(toLower),split(''),replace(/\s{2,}/ig,''));

    dasherize('Theworldisavampire');

    //'the-world-is-a-vampire'

    Thetracefunctionallowsustoviewthedataatacertainpointfordebuggingpurposes.Languageslikehaskellandpurescripthavesimilarfunctionsforeaseofdevelopment.

    Compositionwillbeourtoolforconstructingprogramsand,asluckwouldhaveit,isbackedbyapowerfultheorythatensuresthingswillworkoutforus.Let'sexaminethistheory.

    Categorytheoryisanabstractbranchofmathematicsthatcanformalizeconceptsfromseveraldifferentbranchessuchassettheory,typetheory,grouptheory,logic,andmore.Itprimarilydealswithobjects,morphisms,andtransformations,whichmirrorsprogrammingquiteclosely.Hereisachartofthesameconceptsasviewedfromeachseparatetheory.

    Sorry,Ididn'tmeantofrightenyou.Idon'texpectyoutobeintimatelyfamiliarwithalltheseconcepts.Mypointistoshowyouhowmuchduplicationwehavesoyoucanseewhycategorytheoryaimstounifythesethings.

    Incategorytheory,wehavesomethingcalled...acategory.Itisdefinedasacollectionwiththefollowingcomponents:

    Categorytheory

    mostly-adequate-guide

    28Chapter5:CodingbyComposing

  • AcollectionofobjectsAcollectionofmorphismsAnotionofcompositiononthemorphismsAdistinguishedmorphismcalledidentity

    Categorytheoryisabstractenoughtomodelmanythings,butlet'sapplythistotypesandfunctions,whichiswhatwecareaboutatthemoment.

    AcollectionofobjectsTheobjectswillbedatatypes.Forinstance,String,Boolean,Number,Object,etc.Weoftenviewdatatypesassetsofallthepossiblevalues.OnecouldlookatBooleanasthesetof[true,false]andNumberasthesetofallpossiblenumericvalues.Treatingtypesassetsisusefulbecausewecanusesettheorytoworkwiththem.

    AcollectionofmorphismsThemorphismswillbeourstandardeverydaypurefunctions.

    AnotionofcompositiononthemorphismsThis,asyoumayhaveguessed,isourbrandnewtoy-compose.We'vediscussedthatourcomposefunctionisassociativewhichisnocoincidenceasitisapropertythatmustholdforanycompositionincategorytheory.

    Hereisanimagedemonstratingcomposition:

    Hereisaconcreteexampleincode:

    varg=function(x){returnx.length;};varf=function(x){returnx===4;};varisFourLetterWord=compose(f,g);

    AdistinguishedmorphismcalledidentityLet'sintroduceausefulfunctioncalledid.Thisfunctionsimplytakessomeinputandspitsitbackatyou.Takealook:

    varid=function(x){returnx;};

    Youmightaskyourself"Whatinthebloodyhellisthatusefulfor?".We'llmakeextensiveuseofthisfunctioninthefollowing

    mostly-adequate-guide

    29Chapter5:CodingbyComposing

  • chapters,butfornowthinkofitasafunctionthatcanstandinforourvalue-afunctionmasqueradingaseverydaydata.

    idmustplaynicelywithcompose.Hereisapropertythatalwaysholdsforeveryunary(unary:aoneargumentfunction)functionf:

    //identitycompose(id,f)==compose(f,id)==f;//true

    Hey,it'sjustliketheidentitypropertyonnumbers!Ifthat'snotimmediatelyclear,takesometimewithit.Understandthefutility.We'llbeseeingidusedallovertheplacesoon,butfornowweseeit'safunctionthatactsasastandinforagivenvalue.Thisisquiteusefulwhenwritingpointfreecode.

    Sothereyouhaveit,acategoryoftypesandfunctions.Ifthisisyourfirstintroduction,Iimagineyou'restillalittlefuzzyonwhatacategoryisandwhyit'suseful.Wewillbuilduponthisknowledgethroughoutthebook.Asofrightnow,inthischapter,onthisline,youcanatleastseeitasprovidinguswithsomewisdomregardingcomposition-namely,theassociativityandidentityproperties.

    Whataresomeothercategories,youask?Well,wecandefineonefordirectedgraphswithnodesbeingobjects,edgesbeingmorphisms,andcompositionjustbeingpathconcatenation.WecandefinewithNumbersasobjectsand>=asmorphisms(actuallyanypartialortotalordercanbeacategory).Thereareheapsofcategories,butforthepurposesofthisbook,we'llonlyconcernourselveswiththeonedefinedabove.Wehavesufficientlyskimmedthesurfaceandmustmoveon.

    Compositionconnectsourfunctionstogetherlikeaseriesofpipes.Datawillflowthroughourapplicationasitmust-purefunctionsareinputtooutputafterallsobreakingthischainwoulddisregardoutput,renderingoursoftwareuseless.

    Weholdcompositionasadesignprincipleaboveallothers.Thisisbecauseitkeepsourappsimpleandreasonable.Categorytheorywillplayabigpartinapparchitecture,modellingsideeffects,andensuringcorrectness.

    Wearenowatapointwhereitwouldserveuswelltoseesomeofthisinpractice.Let'smakeanexampleapplication.

    Chapter6:ExampleApplication

    var_=require('ramda');varaccounting=require('accounting');

    //ExampleDatavarCARS=[{name:"FerrariFF",horsepower:660,dollar_value:700000,in_stock:true},{name:"SpykerC12Zagato",horsepower:650,dollar_value:648000,in_stock:false},{name:"JaguarXKR-S",horsepower:550,dollar_value:132000,in_stock:false},{name:"AudiR8",horsepower:525,dollar_value:114200,in_stock:false},{name:"AstonMartinOne-77",horsepower:750,dollar_value:1850000,in_stock:true},{name:"PaganiHuayra",horsepower:700,dollar_value:1300000,in_stock:false}];

    //Exercise1://============//use_.compose()torewritethefunctionbelow.Hint:_.prop()iscurried.varisLastInStock=function(cars){varlast_car=_.last(cars);return_.prop('in_stock',last_car);

    InSummary

    Exercises

    mostly-adequate-guide

    30Chapter5:CodingbyComposing

  • };

    //Exercise2://============//use_.compose(),_.prop()and_.head()toretrievethenameofthefirstcarvarnameOfFirstCar=undefined;

    //Exercise3://============//Usethehelperfunction_averagetorefactoraverageDollarValueasacompositionvar_average=function(xs){return_.reduce(_.add,0,xs)/xs.length;};//

  • Wearegoingtoswitchourmindset.Fromhereonout,we'llstoptellingthecomputerhowtodoitsjobandinsteadwriteaspecificationofwhatwe'dlikeasaresult.I'msureyou'llfinditmuchlessstressfulthantryingtomicromanageeverythingallthetime.

    Declarative,asopposedtoimperative,meansthatwewillwriteexpressions,asopposedtostepbystepinstructions.

    ThinkofSQL.Thereisno"firstdothis,thendothat".Thereisoneexpressionthatspecifieswhat'dlikefromthedatabase.Wedon'tdecidehowtodothework,itdoes.WhenthedatabaseisupgradedandtheSQLengineoptimized,wedon'thavetochangeourquery.Thisisbecausetherearemanywaystointerpretourspecificationandachievethesameresult.

    Forsomefolks,myselfincluded,it'shardtograsptheconceptofdeclarativecodingatfirstsolet'spointoutafewexamplestogetafeelforit.

    //imperativevarmakes=[];for(i=0;i<cars.length;i++){makes.push(cars[i].make);}

    //declarativevarmakes=cars.map(function(car){returncar.make;});

    Theimperativeloopmustfirstinstantiatethearray.Theinterpretermustevaluatethisstatementbeforemovingon.Thenitdirectlyiteratesthroughthelistofcars,manuallyincreasingacounterandshowingitsbitsandpiecestousinavulgardisplayofexplicititeration.

    Themapversionisoneexpression.Itdoesnotrequireanyorderofevaluation.Thereismuchfreedomhereforhowthemapfunctioniteratesandhowthereturnedarraymaybeassembled.Itspecifieswhat,nothow.Thus,itwearstheshinydeclarativesash.

    Inadditiontobeingclearerandmoreconcise,themapfunctionmaybeoptimizedatwillandourpreciousapplicationcodeneedn'tchange.

    Forthoseofyouwhoarethinking"Yes,butit'smuchfastertodotheimperativeloop",IsuggestyoueducateyourselfonhowtheJIToptimizesyourcode.Here'saterrificvideothatmayshedsomelight

    Hereisanotherexample.

    //imperativevarauthenticate=function(form){varuser=toUser(form);returnlogIn(user);};

    //declarativevarauthenticate=compose(logIn,toUser);

    Thoughthere'snothingnecessarilywrongwiththeimperativeversion,thereisstillanencodedstep-by-stepevaluationbakedin.Thecomposeexpressionsimplystatesafact:AuthenticationisthecompositionoftoUserandlogIn.Again,this

    Chapter6:ExampleApplication

    Declarativecoding

    mostly-adequate-guide

    32Chapter6:ExampleApplication

    https://www.youtube.com/watch?v=65-RbBwZQdU

  • leaveswiggleroomforsupportcodechangesandresultsinourapplicationcodebeingahighlevelspecification.

    Becausewearenotencodingorderofevaluation,declarativecodinglendsitselftoparallelcomputing.ThiscoupledwithpurefunctionsiswhyFPisagoodoptionfortheparallelfuture-wedon'treallyneedtodoanythingspecialtoachieveparallel/concurrentsystems.

    Wewillnowbuildanexampleapplicationinadeclarative,composableway.We'llstillcheatandusesideeffectsfornow,butwe'llkeepthemminimalandseparatefromourpurecodebase.Wearegoingtobuildabrowserwidgetthatsucksinflickrimagesanddisplaysthem.Let'sstartbyscaffoldingtheapp.Here'sthehtml:

    Andhere'stheflickr.jsskeleton:

    requirejs.config({paths:{ramda:'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',jquery:'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'}});

    require(['ramda','jquery'],function(_,$){vartrace=_.curry(function(tag,x){console.log(tag,x);returnx;});//appgoeshere});

    We'repullinginramdainsteadoflodashorsomeotherutilitylibrary.Itincludescompose,curry,andmore.I'veusedrequirejs,whichmayseemlikeoverkill,butwe'llbeusingitthroughoutthebookandconsistencyiskey.Also,I'vestartedusoffwithournicetracefunctionforeasydebugging.

    Nowthatthat'soutoftheway,ontothespec.Ourappwilldo4things.

    1. Constructaurlforourparticularsearchterm2. Maketheflickrapicall3. Transformtheresultingjsonintohtmlimages4. Placethemonthescreen

    Thereare2impureactionsmentionedabove.Doyouseethem?Thosebitsaboutgettingdatafromtheflickrapiandplacingitonthescreen.Let'sdefinethosefirstsowecanquarantinethem.

    varImpure={getJSON:_.curry(function(callback,url){$.getJSON(url,callback);

    Aflickroffunctionalprogramming

    mostly-adequate-guide

    33Chapter6:ExampleApplication

    http://ramdajs.com

  • }),

    setHtml:_.curry(function(sel,html){$(sel).html(html);})};

    Herewe'vesimplywrappedjQuery'smethodstobecurriedandwe'veswappedtheargumentstoamorefavorableposition.I'venamespacedthemwithImpuresoweknowthesearedangerousfunctions.Inafutureexample,wewillmakethesetwofunctionspure.

    NextwemustconstructaurltopasstoourImpure.getJSONfunction.

    varurl=function(term){return'https://api.flickr.com/services/feeds/photos_public.gne?tags='+term+'&format=json&jsoncallback=?';};

    Therearefancyandoverlycomplexwaysofwritingurlpointfreeusingmonoids(we'lllearnabouttheselater)orcombinators.We'vechosentostickwithareadableversionandassemblethisstringinthenormalpointfulfashion.

    Let'swriteanappfunctionthatmakesthecallandplacesthecontentsonthescreen.

    varapp=_.compose(Impure.getJSON(trace("response")),url);

    app("cats");

    Thiscallsoururlfunction,thenpassesthestringtoourgetJSONfunction,whichhasbeenpartiallyappliedwithtrace.Loadingtheappwillshowtheresponsefromtheapicallintheconsole.

    We'dliketoconstructimagesoutofthisjson.Itlookslikethesrcsareburiedinitemstheneachmedia'smproperty.

    mostly-adequate-guide

    34Chapter6:ExampleApplication

  • Anyhow,togetatthesenestedpropertieswecanuseaniceuniversalgetterfunctionfromramdacalled_.prop().Here'sahomegrownversionsoyoucanseewhat'shappening:

    varprop=_.curry(function(property,object){returnobject[property];});

    It'squitedullactually.Wejustuse[]syntaxtoaccessapropertyonwhateverobject.Let'susethistogetatoursrcs.

    varmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varsrcs=_.compose(_.map(mediaUrl),_.prop('items'));

    Oncewegathertheitems,wemustmapoverthemtoextracteachmediaurl.Thisresultsinanicearrayofsrcs.Let'shookthisuptoourappandprintthemonthescreen.

    varrenderImages=_.compose(Impure.setHtml("body"),srcs);varapp=_.compose(Impure.getJSON(renderImages),url);

    Allwe'vedoneismakeanewcompositionthatwillcalloursrcsandsetthebodyhtmlwiththem.We'vereplacedthetracecallwithrenderImagesnowthatwehavesomethingtorenderbesidesrawjson.Thiswillcrudelydisplayoursrcsdirectlyinthebody.

    Ourfinalstepistoturnthesesrcsintobonafideimages.Inabiggerapplication,we'duseatemplate/domlibrarylikeHandlebarsorReact.Forthisapplicationthough,weonlyneedanimgtagsolet'sstickwithjQuery.

    varimg=function(url){return$('',{src:url});};

    jQuery'shtml()methodwillacceptanarrayoftags.WeonlyhavetotransformoursrcsintoimagesandsendthemalongtosetHtml.

    varimages=_.compose(_.map(img),srcs);varrenderImages=_.compose(Impure.setHtml("body"),images);varapp=_.compose(Impure.getJSON(renderImages),url);

    Andwe'redone!

    mostly-adequate-guide

    35Chapter6:ExampleApplication

  • Hereisthefinishedscript:

    requirejs.config({paths:{ramda:'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',jquery:'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'}});

    require(['ramda','jquery'],function(_,$){//////////////////////////////////////////////Utils

    varImpure={getJSON:_.curry(function(callback,url){$.getJSON(url,callback);}),

    setHtml:_.curry(function(sel,html){$(sel).html(html);})};

    varimg=function(url){return$('',{src:url});};

    vartrace=_.curry(function(tag,x){console.log(tag,x);returnx;});

    ////////////////////////////////////////////

    varurl=function(t){return'http://api.flickr.com/services/feeds/photos_public.gne?tags='+t+'&format=json&jsoncallback=?';};

    mostly-adequate-guide

    36Chapter6:ExampleApplication

  • varmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varsrcs=_.compose(_.map(mediaUrl),_.prop('items'));

    varimages=_.compose(_.map(img),srcs);

    varrenderImages=_.compose(Impure.setHtml("body"),images);

    varapp=_.compose(Impure.getJSON(renderImages),url);

    app("cats");});

    Nowlookatthat.Abeautifullydeclarativespecificationofwhatthingsare,nothowtheycometobe.Wenowvieweachlineasanequationwithpropertiesthathold.Wecanusethesepropertiestoreasonaboutourapplicationandrefactor.

    Thereisanoptimizationavailable-wemapovereachitemtoturnitintoamediaurl,thenwemapagainoverthosesrcstoturnthemintoimgtags.Thereisalawregardingmapandcomposition:

    //map'scompositionlawvarlaw=compose(map(f),map(g))==map(compose(f,g));

    Wecanusethispropertytooptimizeourcode.Let'shaveaprincipledrefactor.

    //originalcodevarmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varsrcs=_.compose(_.map(mediaUrl),_.prop('items'));

    varimages=_.compose(_.map(img),srcs);

    Let'slineupourmaps.Wecaninlinethecalltosrcsinimagesthankstoequationalreasoningandpurity.

    varmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varimages=_.compose(_.map(img),_.map(mediaUrl),_.prop('items'));

    Nowthatwe'velinedupourmap'swecanapplythecompositionlaw.

    varmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varimages=_.compose(_.map(_.compose(img,mediaUrl)),_.prop('items'));

    Nowthebuggerwillonlylooponcewhileturningeachitemintoanimg.Let'sjustmakeitalittlemorereadablebyextractingthefunctionout.

    varmediaUrl=_.compose(_.prop('m'),_.prop('media'));

    varmediaToImg=_.compose(img,mediaUrl);

    varimages=_.compose(_.map(mediaToImg),_.prop('items'));

    APrincipledRefactor

    mostly-adequate-guide

    37Chapter6:ExampleApplication

  • Wehaveseenhowtoputournewskillsintousewithasmall,butrealworldapp.We'veusedourmathematicalframeworktoreasonaboutandrefactorourcode.Butwhatabouterrorhandlingandcodebranching?Howcanwemakethewholeapplicationpureinsteadofmerelynamespacingdestructivefunctions?Howcanwemakeourappsaferandmoreexpressive?Thesearethequestionswewilltackleinpart2.

    Chapter7:Hindley-MilnerandMe

    InSummary

    mostly-adequate-guide

    38Chapter6:ExampleApplication

  • Ifyou'renewtothefunctionalworld,itwon'tbelongbeforeyoufindyourselfkneedeepintypesignatures.Typesarethemetalanguagethatenablespeoplefromalldifferentbackgroundstocommunicatesuccinctlyandeffectively.Forthemostpart,they'rewrittenwithasystemcalled"Hindley-Milner",whichwe'llbeexaminingtogetherinthischapter.

    Whenworkingwithpurefunctions,typesignatureshaveanexpressivepowertowhichtheEnglishlanguagecannotholdacandle.Thesesignatureswhisperinyoureartheintimatesecretsofafunction.Inasingle,compactline,theyexposebehaviourandintention.Wecanderive"freetheorems"fromthem.Typescanbeinferredsothere'snoneedforexplicittypeannotations.Theycanbetunedtofinepointprecisionorleftgeneralandabstract.Theyarenotonlyusefulforcompiletimechecks,butalsoturnouttobethebestpossibledocumentationavailable.Typesignaturesthusplayanimportantpartinfunctionalprogramming-muchmorethanyoumightfirstexpect.

    JavaScriptisadynamiclanguage,butthatdoesnotmeanweavoidtypesalltogether.We'restillworkingwithstrings,numbers,booleans,andsoon.It'sjustthatthereisn'tanylanguagelevelintegrationsoweholdthisinformationinourheads.Nottoworry,sincewe'reusingsignaturesfordocumentation,wecanusecommentstoserveourpurpose.

    TherearetypecheckingtoolsavailableforJavaScriptsuchasFloworthetypeddialect,TypeScript.Theaimofthisbookistoequiponewiththetoolstowritefunctionalcodesowe'llstickwiththestandardtypesystemusedacrossFPlanguages.

    Fromthedustypagesofmathbooks,acrossthevastseaofwhitepapers,amongstcasualsaturdaymorningblogposts,downintothesourcecodeitself,wefindHindley-Milnertypesignatures.Thesystemisquitesimple,butwarrantsaquickexplanationandsomepracticetofullyabsorbthelittlelanguage.

    //capitalize::String->Stringvarcapitalize=function(s){returntoUpperCase(head(s))+toLowerCase(tail(s));}

    capitalize("smurf");//=>"Smurf"

    Here,capitalizetakesaStringandreturnsaString.Nevermindtheimplementation,it'sthetypesignaturewe'reinterestedin.

    InHM,functionsarewrittenasa->bwhereaandbarevariablesforanytype.Sothesignaturesforcapitalizecanbereadas"afunctionfromStringtoString".Inotherwords,ittakesaStringasitsinputandreturnsaStringasitsoutput.

    Let'slookatsomemorefunctionsignatures:

    //strLength::String->NumbervarstrLength=function(s){returns.length;}

    //join::String->[String]->Stringvarjoin=curry(function(what,xs){returnxs.join(what);

    Hindley-MilnerandMe

    What'syourtype?

    Talesfromthecryptic

    mostly-adequate-guide

    39Chapter7:Hindley-MilnerandMe

    http://flowtype.org/http://www.typescriptlang.org/

  • });

    //match::Regex->String->[String]varmatch=curry(function(reg,s){returns.match(reg);});

    //replace::Regex->String->String->Stringvarreplace=curry(function(reg,sub,s){returns.replace(reg,sub);});

    strLengthisthesameideaasbefore:wetakeaStringandreturnyouaNumber.

    Theothersmightperplexyouatfirstglance.Withoutfullyunderstandingthedetails,youcouldalwaysjustviewthelasttypeasthereturnvalue.Soformatchyoucaninterpretas:IttakesaRegexandaStringandreturnsyou[String].ButaninterestingthingisgoingonherethatI'dliketotakeamomenttoexplainifImay.

    Formatchwearefreetogroupthesignaturelikeso:

    //match::Regex->(String->[String])varmatch=curry(function(reg,s){returns.match(reg);});

    Ahyes,groupingthelastpartinparenthesisrevealsmoreinformation.NowitisseenasafunctionthattakesaRegexandreturnsusafunctionfromStringto[String].Becauseofcurrying,thisisindeedthecase:giveitaRegexandwegetafunctionbackwaitingforitsStringargument.Ofcourse,wedon'thavetothinkofitthisway,butitisgoodtounderstandwhythelasttypeistheonereturned.

    //match::Regex->(String->[String])

    //onHoliday::String->[String]varonHoliday=match(/holiday/ig);

    Eachargumentpopsonetypeoffthefrontofthesignature.onHolidayismatchthatalreadyhasaRegex.

    //replace::Regex->(String->(String->String))varreplace=curry(function(reg,sub,s){returns.replace(reg,sub);});

    Asyoucanseewiththefullparenthesisonreplace,theextranotationcangetalittlenoisyandredundantsowesimplyomitthem.Wecangivealltheargumentsatonceifwechoosesoit'seasiertojustthinkofitas:replacetakesaRegex,aString,anotherStringandreturnsyouaString.

    Afewlastthingshere:

    //id::a->avarid=function(x){returnx;}

    //map::(a->b)->[a]->[b]varmap=curry(function(f,xs){returnxs.map(f);});

    Theidfunctiontakesanyoldtypeaandreturnssomethingofthesametypea.We'reabletousevariablesintypesjust

    mostly-adequate-guide

    40Chapter7:Hindley-MilnerandMe

  • likeincode.Variablenameslikeaandbareconvention,buttheyarearbitraryandcanbereplacedwithwhatevernameyou'dlike.Iftheyarethesamevariable,theyhavetobethesametype.That'sanimportantrulesolet'sreiterate:a->bcanbeanytypeatoanytypeb,buta->ameansithastobethesametype.Forexample,idmaybeString->StringorNumber->Number,butnotString->Bool.

    mapsimilarlyusestypevariables,butthistimeweintroducebwhichmayormaynotbethesametypeasa.Wecanreaditas:maptakesafunctionfromanytypeatothesameordifferenttypeb,thentakesanarrayofa'sandresultsinanarrayofb's.

    Hopefully,you'vebeenovercomebytheexpressivebeautyinthistypesignature.Itliterallytellsuswhatthefunctiondoesalmostwordforword.It'sgivenafunctionfromatob,anarrayofa,anditdeliversusanarrayofb.Theonlysensiblethingforittodoiscallthebloodyfunctiononeacha.Anythingelsewouldbeaboldfacelie.

    Beingabletoreasonabouttypesandtheirimplicationsisaskillthatwilltakeyoufarinthefunctionalworld.Notonlywillpapers,blogs,docs,etc,becomemoredigestible,butthesignatureitselfwillpracticallylectureyouonitsfunctionality.Ittakespracticetobecomeafluentreader,butifyoustickwithit,heapsofinformationwillbecomeavailabletoyousansRTFMing.

    Here'safewmorejusttoseeifyoucandecipherthemonyourown.

    //head::[a]->avarhead=function(xs){returnxs[0];}

    //filter::(a->Bool)->[a]->[a]varfilter=curry(function(f,xs){returnxs.filter(f);});

    //reduce::(b->a->b)->b->[a]->bvarreduce=curry(function(f,x,xs){returnxs.reduce(f,x);});

    reduceisperhaps,themostexpressiveofall.It'satrickyone,however,sodon'tfeelinadequateshouldyoustrugglewithit.Forthecurious,I'lltrytoexplaininEnglishthoughworkingthroughthesignatureonyourownismuchmoreinstructive.

    Ahem,heregoesnothing....lookingatthesignature,weseethefirstargumentisafunctionthatexpectsab,ana,andproducesab.Wheremightitgettheseasandbs?Well,thefollowingargumentsinthesignatureareabandanarrayofassowecanonlyassumethatthebandeachofthoseaswillbefedin.Wealsoseethattheresultofthefunctionisabsothethinkinghereisourfinalincantationofthepassedinfunctionwillbeouroutputvalue.Knowingwhatreducedoes,wecanstatethattheaboveinvestigationisaccurate.

    Onceatypevariableisintroduced,thereemergesacuriouspropertycalledparametricity(http://en.wikipedia.org/wiki/Parametricity).Thispropertystatesthatafunctionwillactonalltypesinauniformmanner.Let'sinvestigate:

    //head::[a]->a

    Lookingathead,weseethatittakes[a]toa.Besidestheconcretetypearray,ithasnootherinformationavailableand,therefore,itsfunctionalityislimitedtoworkingonthearrayalone.Whatcoulditpossiblydowiththevariableaifitknowsnothingaboutit?Inotherwords,asaysitcannotbeaspecifictype,whichmeansitcanbeanytype,whichleavesuswithafunctionthatmustworkuniformlyforeveryconceivabletype.Thisiswhatparametricityisallabout.Guessingat

    Narrowingthepossibility

    mostly-adequate-guide

    41Chapter7:Hindley-MilnerandMe

    http://en.wikipedia.org/wiki/Parametricity

  • theimplementation,theonlyreasonableassumptionsarethatittakesthefirst,last,orarandomelementfromthatarray.Thenameheadshouldtipusoff.

    Here'sanotherone:

    //reverse::[a]->[a]

    Fromthetypesignaturealone,whatcouldreversepossiblybeupto?Again,itcannotdoanythingspecifictoa.Itcannotchangeatoadifferenttypeorwe'dintroduceab.Canitsort?Well,no,itwouldn'thaveenoughinformationtosorteverypossibletype.Canitre-arrange?Yes,Isupposeitcandothat,butithastodosoinexactlythesamepredictableway.Anotherpossibilityisthatitmaydecidetoremoveorduplicateanelement.Inanycase,thepointis,thepossiblebehaviourismassivelynarrowedbyitspolymorphictype.

    ThisnarrowingofpossibilityallowsustousetypesignaturesearchengineslikeHoogletofindafunctionwe'reafter.Theinformationpackedtightlyintoasignatureisquitepowerfulindeed.

    Besidesdeducingimplementationpossibilities,thissortofreasoninggainsusfreetheorems.WhatfollowsareafewrandomexampletheoremslifteddirectlyfromWadler'spaperonthesubject.

    //head::[a]->acompose(f,head)==compose(head,map(f));

    //filter::(a->Bool)->[a]->[a]compose(map(f),filter(compose(p,f)))==compose(filter(p),map(f));

    Youdon'tneedanycodetogetthesetheorems,theyfollowdirectlyfromthetypes.Thefirstonesaysthatifwegettheheadofourarray,thenrunsomefunctionfonit,thatisequivalentto,andincidentally,muchfasterthan,ifwefirstmap(f)overeveryelementthentaketheheadoftheresult.

    Youmightthink,wellthat'sjustcommonsense.ButlastIchecked,computersdon'thavecommonsense.Indeed,theymusthaveaformalwaytoautomatethesekindofcodeoptimizations.Mathshasawayofformalizingtheintuitive,whichishelpfulamidsttherigidterrainofcomputerlogic.

    Thefiltertheoremissimilar.Itsaysthatifwecomposefandptocheckwhichshouldbefiltered,thenactuallyapplythefviamap(rememberfilter,willnottransformtheelements-itssignatureenforcesthatawillnotbetouched),itwillalwaysbeequivalenttomappingourfthenfilteringtheresultwiththeppredicate.

    Thesearejusttwoexamples,butyoucanapplythisreasoningtoanypolymorphictypesignatureanditwillalwayshold.InJavaScript,therearesometoolsavailabletodeclarerewriterules.Onemightalsodothisviathecomposefunctionitself.Thefruitislowhangingandthepossibilitiesareendless.

    Onelastthingtonoteisthatwecanconstraintypestoaninterface.

    //sort::Orda=>[a]->[a]

    Whatweseeontheleftsideofourfatarrowhereisthestatementofafact:amustbeanOrd.Orinotherwords,amust

    Freeasintheorem

    Constraints

    mostly-adequate-guide

    42Chapter7:Hindley-MilnerandMe

    https://www.haskell.org/hooglehttp://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf

  • implementtheOrdinterface.WhatisOrdandwherediditcomefrom?Inatypedlanguageitwouldbeadefinedinterfacethatsayswecanorderthevalues.Thisnotonlytellsusmoreabouttheaandwhatoursortfunctionisupto,butalsorestrictsthedomain.Wecalltheseinterfacedeclarationstypeconstraints.

    //assertEqual::(Eqa,Showa)=>a->a->Assertion

    Here,wehavetwoconstraints:EqandShow.Thosewillensurethatwecancheckequalityofourasandprintthedifferenceiftheyarenotequal.

    We'llseemoreexamplesofconstraintsandtheideashouldtakemoreshapeinlaterchapters.

    Hindley-Milnertypesignaturesareubiquitousinthefunctionalworld.Thoughtheyaresimpletoreadandwrite,ittakestimetomasterthetechniqueofunderstandingprogramsthroughsignaturesalone.Wewilladdtypesignaturestoeachlineofcodefromhereonout.

    Chapter8:Tupperware

    InSummary

    mostly-adequate-guide

    43Chapter7:Hindley-MilnerandMe

  • We'veseenhowtowriteprogramswhichpipedatathroughaseriesofpurefunctions.Theyaredeclarativespecificationsofbehaviour.Butwhataboutcontrolflow,errorhandling,asynchronousactions,stateand,dareIsay,effects?!Inthischapter,wewilldiscoverthefoundationuponwhichallofthesehelpfulabstractionsarebuilt.

    Firstwewillcreateacontainer.Thiscontainermustholdanytypeofvalue;aziplockthatholdsonlytapiocapuddingisrarelyuseful.Itwillbeanobject,butwewillnotgiveitpropertiesandmethodsintheOOsense.No,wewilltreatitlikeatreasurechest-aspecialboxthatcradlesourvaluabledata.

    varContainer=function(x){this.__value=x;}

    Container.of=function(x){returnnewContainer(x);};

    Hereisourfirstcontainer.We'vethoughtfullynameditContainer.WewilluseContainer.ofasaconstructorwhichsavesusfromhavingtowritethatgodawfulnewkeywordallovertheplace.There'smoretotheoffunctionthanmeetstheeye,butfornow,thinkofitastheproperwaytoplacevaluesintoourcontainer.

    Let'sexamineourbrandnewbox...

    Tupperware

    TheMightyContainer

    mostly-adequate-guide

    44Chapter8:Tupperware

  • Container.of(3)//=>Container(3)

    Container.of("hotdogs")//=>Container("hotdogs")

    Container.of(Container.of({name:"yoda"}))//=>Container(Container({name:"yoda"}))

    Ifyouareusingnode,youwillsee{__value:x}eventhoughwe'vegotourselvesaContainer(x).Chromewilloutputthetypeproperly,butnomatter;aslongasweunderstandwhataContainerlookslike,we'llbefine.Insomeenvironmentsyoucanoverwritetheinspectmethodifyou'dlike,butwewillnotbesothorough.Forthisbook,wewillwritetheconceptualoutputasifwe'doverwritteninspectasit'smuchmoreinstructivethan{__value:x}forpedagogicalaswellasaestheticreasons.

    Let'smakeafewthingsclearbeforewemoveon:

    Containerisanobjectwithoneproperty.Lotsofcontainersjustholdonething,thoughtheyaren'tlimitedtoone.We'vearbitrarilynameditsproperty__value.

    The__valuecannotbeonespecifictypeorourContainerwouldhardlyliveuptothename.

    OncedatagoesintotheContaineritstaysthere.Wecouldgetitoutbyusing.__value,butthatwoulddefeatthepurpose.

    Thereasonswe'redoingthiswillbecomeclearasamasonjar,butfornow,bearwithme.

    Onceourvalue,whateveritmaybe,isinthecontainer,we'llneedawaytorunfunctionsonit.

    //(a->b)->Containera->ContainerbContainer.prototype.map=function(f){returnContainer.of(f(this.__value))}

    Why,it'sjustlikeArray'sfamousmap,exceptwehaveContainerainsteadof[a].Anditworksessentiallythesameway:

    Container.of(2).map(function(two){returntwo+2})//=>Container(4)

    Container.of("flamethrowers").map(function(s){returns.toUpperCase()})//=>Container("FLAMETHROWERS")

    Container.of("bombs").map(_.concat('away')).map(_.prop('length'))//=>Container(10)

    WecanworkwithourvaluewithouteverhavingtoleavetheContainer.Thisisaremarkablething.OurvalueintheContainerishandedtothemapfunctionsowecanfusswithitandafterward,returnedtoitsContainerforsafekeeping.AsaresultofneverleavingtheContainer,wecancontinuetomapaway,runningfunctionsasweplease.Wecanevenchangethetypeaswegoalongasdemonstratedinthelatterofthethreeexamples.

    MyFirstFunctor

    mostly-adequate-guide

    45Chapter8:Tupperware

  • Waitaminute,ifwekeepcallingmap,itappearstobesomesortofcomposition!Whatmathematicalmagicisatworkhere?Wellchaps,we'vejustdiscoveredFunctors.

    AFunctorisatypethatimplementsmapandobeyssomelaws

    Yes,Functorissimplyaninterfacewithacontract.WecouldhavejustaseasilynameditMappable,butnow,where'sthefuninthat?Functorscomefromcategorytheoryandwe'lllookatthemathsindetailtowardtheendofthechapter,butfornow,let'sworkonintuitionandpracticalusesforthisbizarrelynamedinterface.

    Whatreasoncouldwepossiblyhaveforbottlingupavalueandusingmaptogetatit?Theanswerrevealsitselfifwechooseabetterquestion:Whatdowegainfromaskingourcontainertoapplyfunctionsforus?Well,abstractionoffunctionapplication.Whenwemapafunction,weaskthecontainertypetorunitforus.Thisisaverypowerfulconcept,indeed.

    Containerisfairlyboring.Infact,itisusuallycalledIdentityandhasaboutthesameimpactasouridfunction(againthereisamathematicalconnectionwe'lllookatwhenthetimeisright).However,thereareotherfunctors,thatis,container-liketypesthathaveapropermapfunction,whichcanprovideusefulbehaviourwhilstmapping.Let'sdefineonenow.

    varMaybe=function(x){this.__value=x;}

    Maybe.of=function(x){returnnewMaybe(x);}

    Maybe.prototype.isNothing=function(){return(this.__value===null||this.__value===undefined);}

    Maybe.prototype.map=function(f){returnthis.isNothing()?Maybe.of(null):Maybe.of(f(this.__value));}

    Now,MaybelooksalotlikeContainerwithoneminorchange:itwillfirstchecktoseeifithasavaluebeforecallingthesuppliedfunction.Thishastheeffectofsidesteppingthosepeskynullsaswemap(Notethatthisimplementationissimpliedforteaching).

    Maybe.of("MalkovichMalkovich").map(match(/a/ig));//=>Maybe(['a','a'])

    Maybe.of(null).map(match(/a/ig));//=>Maybe(null)

    Maybe.of({name:"Boris"}).map(_.prop("age")).map(add(10));//=>Maybe(null)

    Schrödinger'sMaybe

    mostly-adequate-guide

    46Chapter8:Tupperware

  • Maybe.of({name:"Dinah",age:14}).map(_.prop("age")).map(add(10));//=>Maybe(24)

    Noticeourappdoesn'texplodewitherrorsaswemapfunctionsoverournullvalues.ThisisbecauseMaybewilltakecaretocheckforavalueeachandeverytimeitappliesafunction.

    Thisdotsyntaxisperfectlyfineandfunctional,butforreasonsmentionedinPart1,we'dliketomaintainourpointfreestyle.Asithappens,mapisfullyequippedtodelegatetowhateverfunctoritreceives:

    //map::Functorf=>(a->b)->fa->fbvarmap=curry(function(f,any_functor_at_all){returnany_functor_at_all.map(f);});

    Thisisdelightfulaswecancarryonwithcompositionperusualandmapwillworkasexpected.Thisisthecasewithramda'smapaswell.We'llusedotnotationwhenit'sinstructiveandthepointfreeversionwhenit'sconvenient.Didyounoticethat?I'vesneakilyintroducedextranotationintoourtypesignature.TheFunctorf=>tellsusthatfmustbeaFunctor.Notthatdifficult,butIfeltIshouldmentionit.

    Inthewild,we'lltypicallyseeMaybeusedinfunctionswhichmightfailtoreturnaresult.

    //safeHead::[a]->Maybe(a)varsafeHead=function(xs){returnMaybe.of(xs[0]);};

    varstreetName=compose(map(_.prop('street')),safeHead,_.prop('addresses'));

    streetName({addresses:[]});//Maybe(null)

    streetName({addresses:[{street:"ShadyLn.",number:4201}]});//Maybe("ShadyLn.")

    safeHeadislikeournormal_.head,butwithaddedtypesafety.AcuriousthinghappenswhenMaybeisintroducedintoourcode;weareforcedtodealwiththosesneakynullvalues.ThesafeHeadfunctionishonestandupfrontaboutitspossiblefailure-there'sreallynothingtobeashamedof-andsoitreturnsaMaybetoinformusofthismatter.Wearemorethanmerelyinformed,however,becauseweareforcedtomaptogetatthevaluewewantsinceitistuckedawayinsidetheMaybeobject.Essentially,thisisanullcheckenforcedbythesafeHeadfunctionitself.Wecannowsleepbetteratnightknowinganullvaluewon'trearitsugly,decapitatedheadwhenweleastexpectit.Apislikethiswillupgradeaflimsyapplicationfrompaperandtackstowoodandnails.Theywillguaranteesafersoftware.

    SometimesafunctionmightreturnaMaybe(null)explicitlytosignalfailure.Forinstance:

    //withdraw::Number->Account->Maybe(Account)varwithdraw=curry(function(amount,account){returnaccount.balance>=amount?Maybe.of({balance:account.balance-amount}):Maybe.of(null);});

    //finishTransaction::Account->StringvarfinishTransaction=compose(remainingBalance,updateLedger);//Maybe(String)

    Usecases

    mostly-adequate-guide

    47Chapter8:Tupperware

  • vargetTwenty=compose(map(finishTransaction),withdraw(20));

    getTwenty({balance:200.00});//Maybe("Yourbalanceis$180.00")

    getTwenty({balance:10.00});//Maybe(null)

    withdrawwilltipitsnoseatusandreturnMaybe(null)ifwe'reshortoncash.Thisfunctionalsocommunicatesitsficklenessandleavesusnochoice,buttomapeverythingafterwards.Thedifferenceisthatthenullwasintentionalhere.InsteadofaMaybe(String),wegettheMaybe(null)backtosignalfailureandourapplicationeffectivelyhaltsinitstracks.Thisisimportanttonote:ifthewithdrawfails,thenmapwillsevertherestofourcomputationsinceitdoesn'teverrunthemappedfunctions,namelyfinishTransaction.Thisispreciselytheintendedbehaviouraswe'dprefernottoupdateourledgerorshowanewbalanceifwehadn'tsuccessfullywithdrawnfunds.

    Onethingpeopleoftenmissisthattherewillalwaysbeanendoftheline;someeffectingfunctionthatsendsJSONalong,orprintstothescreen,oraltersourfilesystem,orwhathaveyou.Wecannotdelivertheoutputwithreturn,wemustrunsomefunctionoranothertosenditoutintotheworld.WecanphraseitlikeaZenBuddhistkoan:"Ifaprogramhasnoobservableeffect,doesitevenrun?".Doesitruncorrectlyforitsownsatisfaction?Isuspectitmerelyburnssomecyclesandgoesbacktosleep...

    Ourapplication'sjobistoretrieve,transform,andcarrythatdataalonguntilit'stimetosaygoodbyeandthefunctionwhichdoessomaybemapped,thusthevalueneedn'tleavethewarmwombofitscontainer.Indeed,acommonerroristotrytoremovethevaluefromourMaybeonewayoranotherasifthepossiblevalueinsidewillsuddenlymaterializeandallwillbeforgiven.Wemustunderstanditmaybeabranchofcodewhereourvalueisnotaroundtoliveuptoitsdestiny.Ourcode,muchlikeSchrödinger'scat,isintwostatesatonceandshouldmaintainthatfactuntilthefinalfunction.Thisgivesourcodealinearflowdespitethelogicalbranching.

    Thereis,however,anescapehatch.Ifwewouldratherreturnacustomvalueandcontinueon,wecanusealittlehelpercalledmaybe.

    //maybe::b->(a->b)->Maybea->bvarmaybe=curry(function(x,f,m){returnm.isNothing()?x:f(m.__value);});

    //getTwenty::Account->StringvargetTwenty=compose(maybe("You'rebroke!",finishTransaction),withdraw(20));

    getTwenty({balance:200.00});//"Yourbalanceis$180.00"

    getTwenty({balance:10.00});//"You'rebroke!"

    Wewillnoweitherreturnastaticvalue(ofthesametypethatfinishTransactionreturns)orcontinueonmerrilyfinishingupthetransactionsansMaybe.Withmaybe,wearewitnessingtheequivalentofanif/elsestatementwhereaswithmap,theimperativeanalogwouldbe:if(x!==null){returnf(x)}.

    TheintroductionofMaybecancausesomeinitialdiscomfort.UsersofSwiftandScalawillknowwhatImeanasit'sbakedrightintothecorelibrariesundertheguiseofOption(al).Whenpushedtodealwithnullchecksallthetime(andthere

    Releasingthevalue

    mostly-adequate-guide

    48Chapter8:Tupperware

  • aretimesweknowwithabsolutecertaintythevalueexists),mostpeoplecan'thelp,butfeelit'satadlaborious.However,withtime,itwillbecomesecondnatureandyou'lllikelyappreciatethesafety.Afterall,mostofthetimeitwillpreventcutcornersandsaveourhides.

    Writingunsafesoftwareisliketakingcaretopainteacheggwithpastelsbeforehurlingitintotraffic;likebuildingaretirementhomewithmaterialswarnedagainstbythreelittlepigs.ItwilldouswelltoputsomesafetyintoourfunctionsandMaybehelpsusdojustthat.

    I'dberemissifIdidn'tmentionthatthe"real"implementationwillsplitMaybeintotwotypes:oneforsomethingandtheotherfornothing.Thisallowsustoobeyparametricityinmapsovalueslikenullandundefinedcanstillbemappedoverandtheuniversalqualificationofthevalueinafunctorwillberespected.You'lloftenseetypeslikeSome(x)/NoneorJust(x)/NothinginsteadofaMaybethatdoesanullcheckonitsvalue.

    Itmaycomeasashock,butthrow/catchisnotverypure.Whenanerroristhrown,insteadofreturninganoutputvalue,wesoundthealarms!Thefunctionattacks,spewingthousandsof0'sand1'slikeshields&spearsinanelectricbattleagainstourintrudinginput.WithournewfriendEither,wecandobetterthantodeclarewaroninput,wecanrespondwithapolitemessage.Let'stakealook:

    varLeft=function(x){this.__value=x;}

    Left.of=function(x){returnnewLeft(x);}

    Left.prototype.map=function(f){returnthis;}

    varRight=function(x){this.__value=x;}

    Right.of=function(x){returnnewRight(x);}

    Right.prototype.map=function(f){returnRight.of(f(this.__value));}

    LeftandRightaretwosubclassesofanabstracttypewecallEither.I'veskippedtheceremonyofcreatingtheEithersuperclassaswewon'teveruseit,butit'sgoodtobeaware.Nowthen,there'snothingnewherebesidesthetwotypes.Let'sseehowtheyact:

    PureErrorHandling

    mostly-adequate-guide

    49Chapter8:Tupperware

  • Right.of("rain").map(function(str){return"b"+str;});//Right("brain")

    Left.of("rain").map(function(str){return"b"+str;});//Left("rain")

    Right.of({host:'localhost',port:80}).map(_.prop('host'));//Right('localhost')

    Left.of("rollseyes...").map(_.prop("host"));//Left('rollseyes...')

    Leftistheteenagerysortandignoresourrequesttomapoverit.RightwillworkjustlikeContainer(a.k.aIdentity).ThepowercomesfromtheabilitytoembedanerrormessagewithintheLeft.

    Supposewehaveafunctionthatmightnotsucceed.Howaboutwecalculateanagefromabirthdate.WecoulduseMaybe(null)tosignalfailureandbranchourprogram,however,thatdoesn'ttellusmuch.Perhaps,we'dliketoknowwhyitfailed.Let'swritethisusingEither.

    varmoment=require('moment');

    //getAge::Date->User->Either(String,Number)vargetAge=curry(function(now,user){varbirthdate=moment(user.birthdate,'YYYY-MM-DD');if(!birthdate.isValid())returnLeft.of("Birthdatecouldnotbeparsed");returnRight.of(now.diff(birthdate,'years'));});

    getAge(moment(),{birthdate:'2005-12-12'});//Right(9)

    getAge(moment(),{birthdate:'20010704'});//Left("Birthdatecouldnotbeparsed")

    Now,justlikeMaybe(null),weareshortcircuitingourappwhenwereturnaLeft.Thedifference,isnowwehaveaclueastowhyourprogramhasderailed.SomethingtonoticeisthatwereturnEither(String,Number),whichholdsaStringasitsleftvalueandaNumberasitsRight.Thistypesignatureisabitinformalaswehaven'ttakenthetimetodefineanactualEithersuperclass,however,welearnalotfromthetype.Itinformsusthatwe'reeithergettinganerrormessageortheageback.

    //fortune::Number->Stringvarfortune=compose(concat("Ifyousurvive,youwillbe"),add(1));

    //zoltar::User->Either(String,_)varzoltar=compose(map(console.log),map(fortune),getAge(moment()));

    zoltar({birthdate:'2005-12-12'});//"Ifyousurvive,youwillbe10"//Right(undefined)

    zoltar({birthdate:'balloons!'});//Left("Birthdatecouldnotbeparsed")

    Whenthebirthdateisvalid,theprogramoutputsitsmysticalfortunetothescreenforustobehold.Otherwise,wearehandedaLeftwiththeerrormessageplainasdaythoughstilltuckedawayinitscontainer.Thatactsjustasifwe'dthrownanerror,butinacalm,mildmannerfashionasopposedtolosingitstemperandscreaminglikeachildwhensomethinggoeswrong.

    Inthisexample,wearelogicallybranchingourcontrolflowdependingonthevalidityofthebirthdate,yetitreadsasonelinearmotionfromrighttoleftratherthanclimbingthroughthecurlybracesofaconditionalstatement.Usually,we'dmovetheconsole.logoutofourzoltarfunctionandmapitatthetimeofcalling,butit'shelpfultoseehowtheRightbranch

    mostly-adequate-guide

    50Chapter8:Tupperware

  • differs.Weuse_intherightbranch'stypesignaturetoindicateit'savaluethatshouldbeignored(Insomebrowsersyouhavetouseconsole.log.bind(console)touseitfirstclass).

    I'dliketotakethisopportunitytopointoutsomethingyoumayhavemissed:fortune,despiteitsusewithEitherinthisexample,iscompletelyignorantofanyfunctorsmillingabout.ThiswasalsothecasewithfinishTransactioninthepreviousexample.Atthetimeofcalling,afunctioncanbesurroundedbymap,whichtransformsitfromanon-functoryfunctiontoafunctoryone,ininformalterms.Wecallthisprocesslifting.Functionstendtobebetteroffworkingwithnormaldatatypesratherthancontainertypes,thenliftedintotherightcontainerasdeemednecessary.Thisleadstosimpler,morereusablefunctionsthatcanbealteredtoworkwithanyfunctorondemand.

    Eitherisgreatforcasualerrorslikevalidationaswellasmoreserious,stoptheshowerrorslikemissingfilesorbrokensockets.TryreplacingsomeoftheMaybeexampleswithEithertogivebetterfeedback.

    Now,Ican'thelp,butfeelI'vedoneEitheradisservicebyintroducingitasmerelyacontainerforerrormessages.Itcaptureslogicaldisjunction(a.k.a||)inatype.ItalsoencodestheideaofaCoproductfromcategorytheory,whichwon'tbetouchedoninthisbook,butiswellworthreadinguponasthere'spropertiestobeexploited.Itisthecanonicalsumtype(ordisjointunionofsets)becauseitsamountofpossibleinhabitantsisthesumofthetwocontainedtypes(Iknowthat'sabithandwavysohere'sagreatarticle.TherearemanythingsEithercanbe,butasafunctor,itisusedforitserrorhandling.

    JustlikewithMaybe,wehavelittleeither,whichbehavessimilarly,buttakestwofunctionsinsteadofoneandastaticvalue.Eachfunctionshouldreturnthesametype:

    //either::(a->c)->(b->c)->Eitherab->cvareither=curry(function(f,g,e){switch(e.constructor){caseLeft:returnf(e.__value);caseRight:returng(e.__value);}});

    //zoltar::User->_varzoltar=compose(console.log,either(id,fortune),getAge(moment()));

    zoltar({birthdate:'2005-12-12'});//"Ifyousurvive,youwillbe10"//undefined

    zoltar({birthdate:'balloons!'});//"Birthdatecouldnotbeparsed"//undefined

    Finally,auseforthatmysteriousidfunction.ItsimplyparrotsbackthevalueintheLefttopasstheerrormessagetoconsole.log.We'vemadeourfortunetellingappmorerobustbyenforcingerrorhandlingfromwithingetAge.Weeitherslaptheuserwithahardtruthlikeahighfivefromapalmreaderorwecarryonwithourprocess.Andwiththat,we'rereadytomoveontoanentirelydifferenttypeoffunctor.

    OldMcDonaldhadEffects...

    mostly-adequate-guide

    51Chapter8:Tupperware

    https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/sum-types

  • Inourchapteraboutpuritywesawapeculiarexampleofapurefunction.Thisfunctioncontainedaside-effect,butwedubbeditpurebywrappingitsactioninanotherfunction.Here'sanotherexampleofthis:

    //getFromStorage::String->(_->String)vargetFromStorage=function(key){returnfunction(){returnlocalStorage[key];}}

    Hadwenotsurroundeditsgutsinanotherfunction,getFromStoragewouldvaryitsoutputdependingonexternalcircumstance.Withthesturdywrapperinplace,wewillalwaysgetthesameoutputperinput:afunctionthat,whencalled,willretrieveaparticularitemfromlocalStorage.Andjustlikethat(maybethrowinafewHailMary's)we'veclearedourconscienceandallisforgiven.

    Except,thisisn'tparticularlyusefulnowisit.Likeacollectableactionfigureinitsoriginalpackaging,wecan'tactuallyplaywithit.Ifonlytherewereawaytoreachinsideofthecontainerandgetatitscontents...EnterIO.

    varIO=function(f){this.__value=f;}

    IO.of=function(x){returnnewIO(function(){returnx;});}

    IO.prototype.map=function(f){returnnewIO(_.compose(f,this.__value));}

    IOdiffersfromthepreviousfunctorsinthatthe__valueisalwaysafunction.Wedon'tthinkofits__valueasafunction,however-thatisanimplementationdetailandwebestignoreit.WhatishappeningisexactlywhatwesawwiththegetFromStorageexample:IOdelaystheimpureactionbycapturingitinafunctionwrapper.Assuch,wethinkofIOascontainingthereturnvalueofthewrappedactionandnotthewrapperitself.Thisisapparentintheoffunction:wehaveanIO(x),theIO(function(){returnx})isjustnecessarytoavoidevaluation.

    Let'sseeitinuse:

    mostly-adequate-guide

    52Chapter8:Tupperware

  • //io_window_::IOWindowvario_window=newIO(function(){returnwindow;});

    io_window.map(function(win){returnwin.innerWidth});//IO(1430)

    io_window.map(_.prop('location')).map(_.prop('href')).map(split('/'));//IO(["http:","","localhost:8000","blog","posts"])

    //$::String->IO[DOM]var$=function(selector){returnnewIO(function(){returndocument.querySelectorAll(selector);});}

    $('#myDiv').map(head).map(function(div){returndiv.innerHTML;});//IO('Iamsomeinnerhtml')

    Here,io_windowisanactualIOthatwecanmapoverstraightaway,whereas$isafunctionthatreturnsanIOafteritscalled.I'vewrittenouttheconceptualreturnvaluestobetterexpresstheIO,though,inreality,itwillalwaysbe{__value:[Function]}.WhenwemapoverourIO,westickthatfunctionattheendofacompositionwhich,inturn,becomesthenew__valueandsoon.Ourmappedfunctionsdonotrun,theygettackedontheendofacomputationwe'rebuildingup,functionbyfunction,likecarefullyplacingdominoesthatwedon'tdaretipover.TheresultisreminiscentofGangofFour'scommandpatternoraqueue.

    Takeamomenttochannelyourfunctorintuition.Ifweseepasttheimplementationdetails,weshouldfeelrightathomemappingoveranycontainernomatteritsquirksoridiosyncrasies.Wehavethefunctorlaws,whichwewillexploretowardtheendofthechapter,tothankforthispseudo-psychicpower.Atanyrate,wecanfinallyplaywithimpurevalueswithoutsacrificingourpreciouspurity.

    Now,we'vecagedthebeast,butwe'llstillhavetosetitfreeatsomepoint.MappingoverourIOhasbuiltupamightyimpurecomputationandrunningitissurelygoingtodisturbthepeace.Sowhereandwhencanwepullthetrigger?IsitevenpossibletorunourIOandstillwearwhiteatourwedding?Theanswerisyes,ifweputtheonusonthecallingcode.Ourpurecode,despitethenefariousplottingandscheming,maintainsitsinnocenceandit'sthecallerwhogetsburdenedwiththeresponsibilityofactuallyrunningtheeffects.Let'sseeanexampletomakethisconcrete.

    //////Ourpurelibrary:lib/params.js///////

    //url::IOStringvarurl=newIO(function(){returnwindow.location.href;});

    //toPairs=String->[[String]]vartoPairs=compose(map(split('=')),split('&'));

    //params::String->[[String]]varparams=compose(toPairs,last,split('?'));

    //findParam::String->IOMaybe[String]varfindParam=function(key){returnmap(compose(Maybe.of,filter(compose(eq(key),head)),params),url);};

    //////Impurecallingcode:main.js///////

    //runitbycalling__value()!findParam("searchTerm").__value();//Maybe([['searchTerm','wafflehouse']])

    OurlibrarykeepsitshandscleanbywrappingurlinanIOandpassingthebucktothecaller.Youmighthavealsonoticedthatwehavestackedourcontainers;it'sperfectlyreasonabletohaveaIO(Maybe([x])),whichisthreefunctorsdeep(Arrayismostd