Functional Programming in JavaScript

261

Transcript of Functional Programming in JavaScript

FunctionalProgramminginJavaScript

TableofContents

FunctionalProgramminginJavaScript

Credits

AbouttheAuthor

AbouttheReviewers

www.PacktPub.com

Supportfiles,eBooks,discountoffersandmore

WhySubscribe?

FreeAccessforPacktaccountholders

Preface

Whatthisbookcovers

Whatyouneedforthisbook

Whothisbookisfor

Conventions

Readerfeedback

Customersupport

Downloadingtheexamplecode

Errata

Piracy

Questions

1.ThePowersofJavaScript’sFunctionalSide–aDemonstration

Introduction

Thedemonstration

Theapplication–ane-commercewebsite

Imperativemethods

Functionalprogramming

Summary

2.FundamentalsofFunctionalProgramming

Functionalprogramminglanguages

Whatmakesalanguagefunctional?

Advantages

Cleanercode

Modularity

Reusability

Reducedcoupling

Mathematicallycorrect

Functionalprogramminginanonfunctionalworld

IsJavaScriptafunctionalprogramminglanguage?

Workingwithfunctions

Self-invokingfunctionsandclosures

Higher-orderfunctions

Purefunctions

Anonymousfunctions

Methodchains

Recursion

Divideandconquer

Lazyevaluation

Thefunctionalprogrammer’stoolkit

Callbacks

Array.prototype.map()

Array.prototype.filter()

Array.prototype.reduce()

Honorablementions

Array.prototype.forEach

Array.prototype.concat

Array.prototype.reverse

Array.prototype.sort

Array.prototype.everyandArray.prototype.some

Summary

3.SettingUptheFunctionalProgrammingEnvironment

Introduction

FunctionallibrariesforJavaScript

Underscore.js

FantasyLand

Bilby.js

Lazy.js

Bacon.js

Honorablementions

Developmentandproductionenvironments

Browsers

Server-sideJavaScript

Afunctionalusecaseintheserver-sideenvironment

CLI

UsingfunctionallibrarieswithotherJavaScriptmodules

FunctionallanguagesthatcompileintoJavaScript

Summary

4.ImplementingFunctionalProgrammingTechniquesinJavaScript

Partialfunctionapplicationandcurrying

Functionmanipulation

Apply,call,andthethiskeyword

Bindingarguments

Functionfactories

Partialapplication

Partialapplicationfromtheleft

Partialapplicationfromtheright

Currying

Functioncomposition

Compose

Sequence–composeinreverse

Compositionsversuschains

Programmingwithcompose

Mostlyfunctionalprogramming

Handlingevents

Functionalreactiveprogramming

Reactivity

Puttingitalltogether

Summary

5.CategoryTheory

Categorytheory

Categorytheoryinanutshell

Typesafety

Objectidentities

Functors

Creatingfunctors

Arraysandfunctors

Functioncompositions,revisited

Monads

Maybes

Promises

Lenses

jQueryisamonad

Implementingcategories

Summary

6.AdvancedTopicsandPitfallsinJavaScript

Recursion

Tailrecursion

TheTail-callelimination

Trampolining

TheY-combinator

Memoization

Variablescope

Scoperesolutions

Globalscope

Localscope

Objectproperties

Closures

Gotchas

Functiondeclarationsversusfunctionexpressionsversusthefunctionconstructor

Functiondeclarations

Functionexpressions

Thefunctionconstructor

Unpredictablebehavior

Summary

7.FunctionalandObject-orientedProgramminginJavaScript

JavaScript–themulti-paradigmlanguage

JavaScript’sobject-orientedimplementation–usingprototypes

Inheritance

JavaScript’sprototypechain

InheritanceinJavaScriptandtheObject.create()method

Mixingfunctionalandobject-orientedprogramminginJavaScript

Functionalinheritance

StrategyPattern

Mixins

Classicalmixins

Functionalmixins

Summary

A.CommonFunctionsforFunctionalProgramminginJavaScript

B.GlossaryofTerms

Index

FunctionalProgramminginJavaScript

FunctionalProgramminginJavaScriptCopyright©2015PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.NeithertheauthornorPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:March2015

Productionreference:1230315

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

BirminghamB32PB,UK

ISBN978-1-78439-822-4

www.packtpub.com

CoverImagebyDanMantyla

CreditsAuthor

DanMantyla

Reviewers

DomDerrien

JoeDorocak

PeterEhrlich

EdwardE.GriebelJr.

CommissioningEditor

JulianUrsell

AcquisitionEditor

OwenRoberts

ContentDevelopmentEditor

KirtiPatil

TechnicalEditor

AbhishekR.Kotian

CopyEditors

AdityaNair

AartiSaldanha

VikrantPhadkey

ProjectCoordinator

NidhiJoshi

Proofreaders

StephenCopestake

MariaGould

PaulHindle

Indexer

TejalDaruwaleSoni

ProductionCoordinator

AparnaBhagat

CoverWork

AparnaBhagat

AbouttheAuthorDanMantylaworksasawebapplicationdeveloperfortheUniversityofKansas.Heenjoyscontributingtoopensourcewebframeworksandwrenchingonmotorcycles.DaniscurrentlylivinginLawrence,Kansas,USA—thebirthplaceofPythonDjangoandhometoLinuxNewsMedia.

Danhasalsoclickedthecoverimage,whichwastakenoutsidehishomeinLawrence,Kansas,USA,wherethesunflowerfieldsareinbloomforonlyoneshortweekinSeptember.

AbouttheReviewersDomDerrienisafullstackwebdeveloperwhohasrecentlybeendefiningapplicationenvironmentswithafocusonhighavailabilityandscalability.He’sbeeninthedevelopmentfieldformorethan15yearsandhasworkedforbigandsmallcompaniesandasanentrepreneur.

He’scurrentlyworkingforthegamecompanyUbisoft,wherehedefinesthenextgenerationservicesplatformforitssuccessfulAAAgames.ToextendthegamerexperienceontotheWebandonmobiles,heprovidestechnicalmeansthataretransparent,efficient,andhighlyflexible.

HavingdevelopedsmartclientsbeforetheintroductionofXHR,usingaframesettagtokeepthecontextandahiddenframeofsize=0todynamicallyexchangedatawithservers,hehadagreatpleasureofreviewingthisbook,whichpushesthelanguagetoitslimits.Hehopesthatitwillhelpdevelopersimprovetheirprogrammingskills.

Iwanttothankmywife,Sophie,andoursons,ErwanandGoulven,withwhomIenjoyapeacefullifeinMontréal,Québec,Canada.

JoeDorocak,whoseInternetmonikerisJoeCodeswell,isaveryexperiencedprogrammer.Heenjoyscreatingreadablecodethatimplementsprojectrequirementsefficientlyandinamannerthatcanbeeasilyunderstood.Heconsiderswritingcodeakintowritingpoetry.

Joeprideshimselfontheabilitytocommunicateclearlyandprofessionally.Heconsidershiscodetobecommunication,notonlywiththemachineplatformsonwhichitruns,butalsowithhumanprogrammerswhomightreaditinthefuture.

JoehasworkedasanemployeeaswellasinacontractualroleformajorbrandssuchasIBM,HP,GTE/Sprint,andothertop-shelfcompanies.Heispresentlyconsultingonweb,mobile,anddesktopapplications,whicharecodedprimarily,butnotexclusively,inPythonandJavaScript.Formoredetailsabouthim,pleasevisithttps://www.linkedin.com/in/joedorocak.

PeterEhrlichtaughthimselfwebprogrammingin2007,andnowworksonperformanceJavaScriptandWebGLatLeapMotion,Inc.Inhissparetime,heenjoysdancing,rockclimbing,andtakingnaps.

EdwardE.GriebelJr.hasbeendevelopingenterprisesoftwareforover20yearsinC,C++,andJava.Hehasabachelorofsciencedegreeincomputerengineering.HeiscurrentlyamiddlewarearchitectataleadingpayrollandfinancialservicesproviderintheU.S.,focusingonsystemsintegrationandUIandserverdevelopment.

www.PacktPub.com

Supportfiles,eBooks,discountoffersandmoreForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www2.packtpub.com/books/subscription/packtlib

DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcanaccess,readandsearchacrossPackt’sentirelibraryofbooks.

WhySubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,printandbookmarkcontentOndemandandaccessibleviawebbrowser

FreeAccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandviewnineentirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.

PrefaceFunctionalprogrammingisastylethatemphasizesandenablesthewritingofsmartercode,whichminimizescomplexityandincreasesmodularity.It’sawayofwritingcleanercodethroughcleverwaysofmutating,combining,andusingfunctions.JavaScriptprovidesanexcellentmediumforthisapproach.JavaScript,theInternet’sscriptinglanguage,isactuallyafunctionallanguageatheart.Bylearninghowtoexposeitstrueidentityasafunctionallanguage,wecanimplementwebapplicationsthatarepowerful,easiertomaintain,andmorereliable.Bydoingthis,JavaScript’soddquirksandpitfallswillsuddenlybecomeclearandthelanguageasawholewillmakeinfinitelymoresense.Learninghowtousefunctionalprogrammingwillmakeyouabetterprogrammerforlife.

ThisbookisaguideforbothnewandexperiencedJavaScriptdeveloperswhoareinterestedinlearningfunctionalprogramming.Withafocusontheprogressionoffunctionalprogrammingtechniques,styles,anddetailedinformationaboutJavaScriptlibraries,thisbookwillhelpyoutowritesmartercodeandbecomeabetterprogrammer.

WhatthisbookcoversChapter1,ThePowersofJavaScript’sFunctionalSide–aDemonstration,setsthepaceofthebookbycreatingasmallwebapplicationwiththehelpofbothtraditionalmethodsandfunctionalprogramming.Itthencomparesthesetwomethodstounderlinetheimportanceoffunctionalprogramming.

Chapter2,FundamentalsofFunctionalProgramming,introducesyoutothecoreconceptsoffunctionalprogrammingaswellasbuilt-inJavaScriptfunctions.

Chapter3,SettingUptheFunctionalProgrammingEnvironment,exploresdifferentJavaScriptlibrariesandhowtheycanbeoptimizedforfunctionalprogramming.

Chapter4,ImplementingFunctionalProgrammingTechniquesinJavaScript,explainsthefunctionalparadigminJavaScript.Itcoversseveralstylesoffunctionalprogramminganddemonstrateshowtheycanbeemployedindifferentscenarios.

Chapter5,CategoryTheory,explainstheconceptofCategoryTheoryindetailandthenimplementsitinJavaScript.

Chapter6,AdvancedTopicsandPitfallsinJavaScript,highlightsvariousdrawbacksyoumayfacewhileprogramminginJavaScript,andthevariouswaystosuccessfullydealwiththem.

Chapter7,FunctionalandObject-orientedProgramminginJavaScript,relatesbothfunctionalandobject-orientedprogrammingtoJavaScript,andshowsyouhowthetwoparadigmscancomplementeachotherandcoexistsidebyside.

AppendixA,CommonFunctionsforFunctionalProgramminginJavaScript,containscommonfunctionsusedtoperformfunctionalprogramminginJavaScript.

AppendixB,GlossaryofTerms,includesaglossaryoftermsusedthroughoutthebook.

WhatyouneedforthisbookOnlyabrowserisneededtogetyouupandrunning.

WhothisbookisforIfyouareaJavaScriptdeveloperinterestedinlearningfunctionalprogramming,lookingforaquantumleaptowardmasteringtheJavaScriptlanguage,orjustwanttobecomeabetterprogrammeringeneral,thenthisbookisidealforyou.Thisguideisaimedatprogrammersinvolvedindevelopingreactivefrontendapplications,server-sideapplicationsthatwranglewithreliabilityandconcurrency,andeverythingelseinbetween.

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Wecanincludeothercontextsthroughtheuseoftheincludedirective.”

Ablockofcodeissetasfollows:

Function.prototype.partialApply=function(){

varfunc=this;

args=Array.prototype.slice.call(arguments);

returnfunction(){

returnfunc.apply(this,args.concat(

Array.prototype.slice.call(arguments)

));

};

};

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

varmessages=['Hi','Hello','Sup','Hey','Hola'];

messages.map(function(s,i){

returnprintSomewhere(s,i*10,i*10);

}).forEach(document.body.appendChild);

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:“ClickingtheNextbuttonmovesyoutothenextscreen.”

NoteWarningsorimportantnotesappearinaboxlikethis.

TipTipsandtricksappearlikethis.

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

Tosendusgeneralfeedback,simplye-mail<[email protected]>,andmentionthebook’stitleinthesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesfromyouraccountathttp://www.packtpub.comforallthePacktPublishingbooksyouhavepurchased.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.

Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<[email protected]>,andwewilldoourbesttoaddresstheproblem.

Chapter1.ThePowersofJavaScript’sFunctionalSide–aDemonstration

IntroductionFordecades,functionalprogramminghasbeenthedarlingofcomputerscienceaficionados,prizedforitsmathematicalpurityandpuzzlingnaturethatkeptithiddenindustycomputerlabsoccupiedbydatascientistsandPhDhopefuls.Butnow,itisgoingthrougharesurgence,thankstomodernlanguagessuchasPython,Julia,Ruby,Clojureand—lastbutnotleast—JavaScipt.

JavaScript,yousay?Theweb’sscriptinglanguage?Yes!

JavaScripthasproventobeanimportanttechnologythatisn’tgoingawayforquiteawhile.Thisislargelyduetothefactthatitiscapableofbeingrebornandextendedwithnewframeworksandlibraries,suchasbackbone.js,jQuery,Dojo,underscore.js,andmanymore.ThisisdirectlyrelatedtoJavaScript’strueidentityasafunctionalprogramminglanguage.AnunderstandingoffunctionalprogrammingwithJavaScriptwillbewelcomeandusefulforalongtimeforprogrammersofanyskilllevel.

Whyso?Functionalprogrammingisverypowerful,robust,andelegant.Itisusefulandefficientonlargedatastructures.ItcanbeveryadvantageoustouseJavaScript—aclient-sidescriptinglanguage,asafunctionalmeanstomanipulatetheDOM,sortAPIresponsesorperformothertasksonincreasinglycomplexwebsites.

Inthisbook,youwilllearneverythingyouneedtoknowaboutfunctionalprogrammingwithJavaScript:howtoempoweryourJavaScriptwebapplicationswithfunctionalprogramming,howtounlockJavaScript’shiddenpowers,andhowtowritebettercodethatisbothmorepowerfuland—becauseitissmaller—easiertomaintain,fastertodownload,andtakeslessoverhead.Youwillalsolearnthecoreconceptsoffunctionalprogramming,howtoapplythemtoJavaScript,howtoside-stepthecaveatsandissuesthatmayarisewhenusingJavaScriptasafunctionallanguage,andhowtomixfunctionalprogrammingwithobject-orientedprogramminginJavaScript.

Butbeforewebegin,let’sperformanexperiment.

ThedemonstrationPerhapsaquickdemonstrationwillbethebestwaytointroducefunctionalprogrammingwithJavaScript.WewillperformthesametaskusingJavaScript—onceusingtraditional,nativemethods,andoncewithfunctionalprogramming.Then,wewillcomparethetwomethods.

Theapplication–ane-commercewebsiteInpursuitofareal-worldapplication,let’ssayweneedane-commercewebapplicationforamail-ordercoffeebeancompany.Theysellseveraltypesofcoffeeandindifferentquantities,bothofwhichaffecttheprice.

ImperativemethodsFirst,let’sgowiththeproceduralroute.Tokeepthisdemonstrationdowntoearth,we’llhavetocreateobjectsthatholdthedata.Thisallowstheabilitytofetchthevaluesfromadatabaseifweneedto.Butfornow,we’llassumethey’restaticallydefined:

//createsomeobjectstostorethedata.

varcolumbian={

name:'columbian',

basePrice:5

};

varfrenchRoast={

name:'frenchroast',

basePrice:8

};

vardecaf={

name:'decaf',

basePrice:6

};

//we'lluseahelperfunctiontocalculatethecost

//accordingtothesizeandprintittoanHTMLlist

functionprintPrice(coffee,size){

if(size=='small'){

varprice=coffee.basePrice+2;

}

elseif(size=='medium'){

varprice=coffee.basePrice+4;

}

else{

varprice=coffee.basePrice+6;

}

//createthenewhtmllistitem

varnode=document.createElement("li");

varlabel=coffee.name+''+size;

vartextnode=document.createTextNode(label+'price:$'+price);

node.appendChild(textnode);

document.getElementById('products').appendChild(node);

}

//nowallweneedtodoiscalltheprintPricefunction

//foreverysinglecombinationofcoffeetypeandsize

printPrice(columbian,'small');

printPrice(columbian,'medium');

printPrice(columbian,'large');

printPrice(frenchRoast,'small');

printPrice(frenchRoast,'medium');

printPrice(frenchRoast,'large');

printPrice(decaf,'small');

printPrice(decaf,'medium');

printPrice(decaf,'large');

Tip

Downloadingtheexamplecode

YoucandownloadexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Asyoucansee,thiscodeisverybasic.Whatifthereweremanymorecoffeestylesthanjustthethreewehavehere?Whatiftherewere20?50?Whatif,inadditiontosize,therewereorganicandnon-organicoptions.Thatcouldincreasethelinesofcodeextremelyquickly!

Usingthismethod,wearetellingthemachinewhattoprintforeachcoffeetypeandforeachsize.Thisisfundamentallywhatiswrongwithimperativecode.

FunctionalprogrammingWhileimperativecodetellsthemachine,step-by-step,whatitneedstodotosolvetheproblem,functionalprogramminginsteadseekstodescribetheproblemmathematicallysothatthemachinecandotherest.

Withamorefunctionalapproach,thesameapplicationcanbewrittenasfollows:

//separatethedataandlogicfromtheinterface

varprintPrice=function(price,label){

varnode=document.createElement("li");

vartextnode=document.createTextNode(label+'price:$'+price);

node.appendChild(textnode);

document.getElementById('products2').appendChild(node);

}

//createfunctionobjectsforeachtypeofcoffee

varcolumbian=function(){

this.name='columbian';

this.basePrice=5;

};

varfrenchRoast=function(){

this.name='frenchroast';

this.basePrice=8;

};

vardecaf=function(){

this.name='decaf';

this.basePrice=6;

};

//createobjectliteralsforthedifferentsizes

varsmall={

getPrice:function(){returnthis.basePrice+2},

getLabel:function(){returnthis.name+'small'}

};

varmedium={

getPrice:function(){returnthis.basePrice+4},

getLabel:function(){returnthis.name+'medium'}

};

varlarge={

getPrice:function(){returnthis.basePrice+6},

getLabel:function(){returnthis.name+'large'}

};

//putallthecoffeetypesandsizesintoarrays

varcoffeeTypes=[columbian,frenchRoast,decaf];

varcoffeeSizes=[small,medium,large];

//buildnewobjectsthatarecombinationsoftheabove

//andputthemintoanewarray

varcoffees=coffeeTypes.reduce(function(previous,current){

varnewCoffee=coffeeSizes.map(function(mixin){

//`plusmix`functionforfunctionalmixins,seeCh.7

varnewCoffeeObj=plusMixin(current,mixin);

returnnewnewCoffeeObj();

});

returnprevious.concat(newCoffee);

},[]);

//we'venowdefinedhowtogetthepriceandlabelforeach

//coffeetypeandsizecombination,nowwecanjustprintthem

coffees.forEach(function(coffee){

printPrice(coffee.getPrice(),coffee.getLabel());

});

Thefirstthingthatshouldbeobviousisthatitismuchmoremodular.Thismakesaddinganewsizeoranewcoffeetypeassimpleasshowninthefollowingcodesnippet:

varperuvian=function(){

this.name='peruvian';

this.basePrice=11;

};

varextraLarge={

getPrice:function(){returnthis.basePrice+10},

getLabel:function(){returnthis.name+'extralarge'}

};

coffeeTypes.push(Peruvian);

coffeeSizes.push(extraLarge);

Arraysofcoffeeobjectsandsizeobjectsare“mixed”together,—thatis,theirmethodsandmembervariablesarecombined—withacustomfunctioncalledplusMixin(seeChapter7,FunctionalandObject-orientedProgramminginJavaScript).Thecoffeetypeclassescontainthemembervariablesandthesizescontainmethodstocalculatethenameandprice.The“mixing”happenswithinamapoperation,whichappliesapurefunctiontoeachelementinanarrayandreturnsanewfunctioninsideareduce()operation—anotherhigher-orderfunctionsimilartothemapfunction,exceptthatalltheelementsinthearrayarecombinedintoone.Finally,thenewarrayofallpossiblecombinationsoftypesandsizesisiteratedthroughwiththeforEach()methodTheforEach()methodisyetanotherhigher-orderfunctionthatappliesacallbackfunctiontoeachobjectinanarray.Inthisexample,weprovideitasananonymousfunctionthatinstantiatestheobjectsandcallstheprintPrice()functionwiththeobject’sgetPrice()andgetLabel()methodsasarguments.

Actually,wecouldmakethisexampleevenmorefunctionalbyremovingthecoffeesvariableandchainingthefunctionstogether—anotherlittletrickinfunctionalprogramming.

coffeeTypes.reduce(function(previous,current){

varnewCoffee=coffeeSizes.map(function(mixin){

//`plusMixin`functionforfunctionalmixins,seeCh.7

varnewCoffeeObj=plusMixin(current,mixin);

returnnewnewCoffeeObj();

});

returnprevious.concat(newCoffee);

},[]).forEach(function(coffee){

printPrice(coffee.getPrice(),coffee.getLabel());

});

Also,thecontrolflowisnotastop-to-bottomastheimperativecodewas.Infunctionalprogramming,themap()functionandotherhigher-orderfunctionstaketheplaceofforandwhileloopsandverylittleimportanceisplacedontheorderofexecution.Thismakesitalittletrickierfornewcomerstotheparadigmtoreadthecodebut,onceyougetthehangofit,it’snothardatalltofollowandyou’llseethatitismuchbetter.

ThisexamplebarelytouchedonwhatfunctionalprogrammingcandoinJavaScript.Throughoutthisbook,youwillseeevenmorepowerfulexamplesofthefunctionalapproach.

SummaryFirst,thebenefitsofadoptingafunctionalstyleareclear.

Second,don’tbescaredoffunctionalprogramming.Yes,itisoftenthoughtofaspurelogicintheformofcomputerlanguage,butwedon’tneedtounderstandLambdacalculustobeabletoapplyittoeverydaytasks.Thefactis,byallowingourprogramstobebrokendownintosmallerpieces,they’reeasiertounderstand,simplertomaintain,andmorereliable.map()andreduce()function’sarelesser-knownbuilt-infunctionsinJavaScript,butwe’lllookatthem.

JavaScriptisascriptinglanguage,interactiveandapproachable.Nocompilingisnecessary.Wedon’tevenneedtodownloadanydevelopmentsoftware,yourfavoritebrowserworksastheinterpreterandasthedevelopmentenvironment.

Interested?Alright,let’sgetstarted!

Chapter2.FundamentalsofFunctionalProgrammingBynow,you’veseenasmallglimpseofwhatfunctionalprogrammingcando.Butwhatexactlyisfunctionalprogramming?Whatmakesonelanguagefunctionalandnotanother?Whatmakesoneprogrammingstylefunctionalandnotanother?

Inthischapter,wewillfirstanswerthesequestionsandthencoverthecoreconceptsoffunctionalprogramming:

UsingfunctionsandarraysforcontrolflowWritingpurefunctions,anonymousfunctions,recursivefunctions,andmorePassingfunctionsaroundlikeobjectsUtilizingthemap(),filter(),andreduce()functions

FunctionalprogramminglanguagesFunctionalprogramminglanguagesarelanguagesthatfacilitatethefunctionalprogrammingparadigm.Attheriskofoversimplifying,wecouldsaythat,ifalanguageincludesthefeaturesrequiredforfunctionalprogramming,thenitisafunctionallanguage—assimpleasthat.Inmostcases,it’stheprogrammingstylethattrulydetermineswhetheraprogramisfunctionalornot.

Whatmakesalanguagefunctional?FunctionalprogrammingcannotbeperformedinC.FunctionalprogrammingcannotbeperformedinJava(withoutalotofcumbersomeworkaroundsfor“almost”functionalprogramming).Thoseandmanymorelanguagessimplydon’tcontaintheconstructstosupportit.Theyarepurelyobject-orientedandstrictlynon-functionallanguages.

Atthesametime,object-orientedprogrammingcannotbeperformedonpurelyfunctionallanguages,suchasScheme,Haskell,andLisp,justtonameafew.

However,therearecertainlanguagesthatsupportbothmodels.Pythonisafamousexample,butthereareothers:Ruby,Julia,and—here’stheonewe’reinterestedin—JavaScript.Howcantheselanguagessupporttwodesignpatternsthatareverydifferentfromeachother?Theycontainthefeaturesrequiredforbothprogrammingparadigms.However,inthecaseofJavaScript,thefunctionalfeaturesaresomewhathidden.

Butreally,it’salittlemoreinvolvedthanthat.Sowhatmakesalanguagefunctional?

Characteristic Imperative Functional

ProgrammingStyle

Performstep-by-steptasksandmanagechangesinstate

Definewhattheproblemisandwhatdatatransformationsareneededtoachievethesolution

StateChanges Important Non-existent

OrderofExecution Important Notasimportant

PrimaryFlowControl

Loops,conditionals,andfunctioncalls Functioncallsandrecursion

PrimaryManipulationUnit

Structuresandclassobjects Functionsasfirst-classobjectsanddatasets

Thesyntaxofthelanguagemustallowforcertaindesignpatterns,suchasaninferredtypesystem,andtheabilitytouseanonymousfunctions.Essentially,thelanguagemustimplementLambdacalculus.Also,theinterpreter’sevaluationstrategyshouldbenon-strictandcall-by-need(alsoknownasdeferredexecution),whichallowsforimmutabledatastructuresandnon-strict,lazyevaluation.

AdvantagesYoucouldsaythattheprofoundenlightenmentyouexperiencewhenyoufinally“getit”willmakelearningfunctionalprogrammingworthit.Anexperiencesuchasthiswillmakeyouabetterprogrammerfortherestofyourlife,whetheryouactuallybecomeafull-timefunctionalprogrammerornot.

Butwe’renottalkingaboutlearningtomeditate;we’retalkingaboutlearninganextremelyusefultoolthatwillmakeyouabetterprogrammer.

Formallyspeaking,whatexactlyarethepracticaladvantagesofusingfunctionalprogramming?

CleanercodeFunctionalprogramsarecleaner,simpler,andsmaller.Thissimplifiesdebugging,testing,andmaintenance.

Forexample,let’ssayweneedafunctionthatconvertsatwo-dimensionalarrayintoaone-dimensionalarray.Usingonlyimperativetechniques,wecouldwriteitthefollowingway:

functionmerge2dArrayIntoOne(arrays){

varcount=arrays.length;

varmerged=newArray(count);

varc=0;

for(vari=0;i<count;++i){

for(varj=0,jlen=arrays[i].length;j<jlen;++j){

merged[c++]=arrays[i][j];

}

}

returnmerged

}

Andusingfunctionaltechniques,itcouldbewrittenasfollows:

varmerge2dArrayIntoOne2=function(arrays){

returnarrays.reduce(function(p,n){

returnp.concat(n);

});

};

Bothofthesefunctionstakethesameinputandreturnthesameoutput.However,thefunctionalexampleismuchmoreconciseandclean.

ModularityFunctionalprogrammingforceslargeproblemstobebrokendownintosmallerinstancesofthesameproblemtobesolved.Thismeansthatthecodeismoremodular.Programsthataremodularareclearlyspecified,easiertodebug,andsimplertomaintain.Testingiseasierbecauseeachpieceofmodularcodecanpotentiallybecheckedforcorrectness.

Reusability

Functionalprogramsshareavarietyofcommonhelperfunctions,duetothemodularityoffunctionalprogramming.You’llfindthatmanyofthesefunctionscanbereusedforavarietyofdifferentapplications.

Manyofthemostcommonfunctionswillbecoveredlaterinthischapter.However,asyouworkasafunctionalprogrammer,youwillinevitablycompileyourownlibraryoflittlefunctionsthatcanbeusedoverandoveragain.Forexample,awell-designedfunctionthatsearchesthroughthelinesofaconfigurationfilecouldalsobeusedtosearchthroughahashtable.

ReducedcouplingCouplingistheamountofdependencybetweenmodulesinaprogram.Becausethefunctionalprogrammerworkstowritefirst-class,higher-order,purefunctionsthatarecompletelyindependentofeachotherwithnosideeffectsonglobalvariables,couplingisgreatlyreduced.Certainly,functionswillunavoidablyrelyoneachother.Butmodifyingonefunctionwillnotchangeanother,solongastheone-to-onemappingofinputstooutputsremainscorrect.

MathematicallycorrectThislastoneisonamoretheoreticallevel.ThankstoitsrootsinLambdacalculus,functionalprogramscanbemathematicallyproventobecorrect.Thisisabigadvantageforresearcherswhoneedtoprovethegrowthrate,timecomplexity,andmathematicalcorrectnessofaprogram.

Let’slookatFibonacci’ssequence.Althoughit’srarelyusedforanythingotherthanaproof-of-concept,itillustratesthisconceptquitewell.ThestandardwayofevaluatingaFibonaccisequenceistocreatearecursivefunctionthatexpressesfibonnaci(n)=fibonnaci(n-2)+fibonnaci(n–1)withabasecasetoreturn1whenn<2,whichmakesitpossibletostoptherecursionandbeginaddingupthevaluesreturnedateachstepintherecursivecallstack.

Thisdescribestheintermediarystepsinvolvedincalculatingthesequence.

varfibonacci=function(n){

if(n<2){

return1;

}

else{

returnfibonacci(n-2)+fibonacci(n-1);

}

}

console.log(fibonacci(8));

//Output:34

However,withthehelpofalibrarythatimplementsalazyexecutionstrategy,anindefinitesequencecanbegeneratedthatstatesthemathematicalequationthatdefinestheentiresequenceofnumbers.Onlyasmanynumbersasneededwillbecomputed.

varfibonacci2=Lazy.generate(function(){

varx=1,

y=1;

returnfunction(){

varprev=x;

x=y;

y+=prev;

returnprev;

};

}());

console.log(fibonacci2.length());//Output:undefined

console.log(fibonacci2.take(12).toArray());//Output:[1,1,2,3,5,8,

13,21,34,55,89,144]

varfibonacci3=Lazy.generate(function(){

varx=1,

y=1;

returnfunction(){

varprev=x;

x=y;

y+=prev;

returnprev;

};

}());

console.log(fibonacci3.take(9).reverse().first(1).toArray());//Output:

[34]

Thesecondexampleisclearlymoremathematicallysound.ItreliesontheLazy.jslibraryofJavaScript.Thereareotherlibrariesthatcanhelphereaswell,suchasSloth.jsandwu.js.ThesewillbecoveredinChapter3,SettingUptheFunctionalProgrammingEnvironment.

FunctionalprogramminginanonfunctionalworldCanfunctionalandnonfunctionalprogrammingbemixedtogether?AlthoughthisisthesubjectofChapter7,Functional&Object-orientedProgramminginJavaScript,itisimportanttogetafewthingsstraightbeforewegoanyfurther.

Thisbookisnotintendedtoteachyouhowtoimplementanentireapplicationthatstrictlyadherestotherigorsofpurefunctionalprogramming.SuchapplicationsarerarelyappropriateoutsideAcademia.Rather,thisbookwillteachyouhowtousefunctionalprogrammingdesignstrategieswithinyourapplicationstocomplementthenecessaryimperativecode.

Forexample,ifyouneedthefirstfourwordsthatonlycontainlettersoutofsometext,theycouldnaivelybewrittenlikethis:

varwords=[],count=0;

text=myString.split('');

for(i=0;count<4,i<text.length;i++){

if(!text[i].match(/[0-9]/)){

words=words.concat(text[i]);

count++;

}

}

console.log(words);

Incontrast,afunctionalprogrammermightwritethemasfollows:

varwords=[];

varwords=myString.split('').filter(function(x){

return(!x.match(/[1-9]+/));

}).slice(0,4);

console.log(words);

Or,withalibraryoffunctionalprogrammingutilities,theycanbesimplifiedevenfurther:

varwords=toSequence(myString).match(/[a-zA-Z]+/).first(4);

Thekeytoidentifyingfunctionsthatcanbewritteninamorefunctionalwayistolookforloopsandtemporaryvariables,suchaswordsandcountinstancesintheprecedingexample.Wecanusuallydoawaywithbothtemporaryvariablesandloopsbyreplacingthemwithhigher-orderfunctions,whichwewillexplorelaterinthischapter.

IsJavaScriptafunctionalprogramminglanguage?Thereisonelastquestionwemustaskourselves.IsJavaScriptafunctionallanguageoranon-functionallanguage?

JavaScriptisarguablytheworld’smostpopularandleastunderstoodfunctionalprogramminglanguage.JavaScriptisafunctionalprogramminglanguageinC-likeclothing.ItssyntaxisundeniablyC-like,meaningitusesC’sblocksyntaxandin-fixordering.Andit’soneoftheworstnamedlanguagesinexistence.Itdoesn’ttakealotofimaginationtoseehowsomanypeoplecanconfuseJavaScriptasbeingrelatedtoJava;somehow,itsnameimpliesthatitshouldbe!Butinrealityithasverylittleincommon

withJava.And,toreallycementtheideathatJavaScriptisanobject-orientedlanguage,librariesandframeworkssuchasDojoandease.jshavebeenhardatworkattemptingtoabstractitandmakeitsuitableforobject-orientedprogramming.JavaScriptcameofageinthe1990swhenOOPwasallthebuzz,andwe’vebeentoldthatJavaScriptisobject-orientedbecausewewantittobesobadly.Butitisnot.

Itstrueidentityismuchmorealignedwithitsancestors:SchemeandLisp,twoclassicfunctionallanguages.JavaScriptisafunctionallanguage,alltheway.Itsfunctionsarefirst-classandcanbenested,ithasclosuresandcompositions,anditallowsforcurryingandmonads.Allofthesearekeytofunctionalprogramming.HereareafewmorereasonswhyJavaScriptisafunctionallanguage:

JavaScript’slexicalgrammarincludestheabilitytopassfunctionsasarguments,hasaninferredtypesystem,andallowsforanonymousfunctions,higher-orderfunctions,closuresandmore.Thesefactsareparamounttoachievingthestructureandbehavioroffunctionalprogramming.Itisnotapureobject-orientedlanguage,withmostobject-orienteddesignpatternsachievedbycopyingthePrototypeobject,aweakmodelforobject-orientedprogramming.EuropeanComputerManufacturersAssociationScript(ECMAScript),JavaScript’sformalandstandardizedspecificationsforimplementation,statesthefollowinginspecification4.2.1:

“ECMAScriptdoesnotcontainproperclassessuchasthoseinC++,Smalltalk,orJava,butrather,supportsconstructorswhichcreateobjects.Inaclass-basedobject-orientedlanguage,ingeneral,stateiscarriedbyinstances,methodsarecarriedbyclasses,andinheritanceisonlyofstructureandbehavior.InECMAScript,thestateandmethodsarecarriedbyobjects,andstructure,behaviorandstateareallinherited.”

Itisaninterpretedlanguage.Sometimescalled“engines”,JavaScriptinterpretersoftencloselyresembleSchemeinterpreters.Botharedynamic,bothhaveflexibledatatypesthateasilycombineandtransform,bothevaluatethecodeintoblocksofexpressions,andbothtreatfunctionssimilarly.

Thatbeingsaid,itistruethatJavaScriptisnotapurefunctionallanguage.What’slackingislazyevaluationandbuilt-inimmutabledata.Thisisbecausemostinterpretersarecall-by-nameandnotcall-by-need.JavaScriptalsoisn’tverygoodwithrecursionduetothewayithandlestailcalls.However,alloftheseissuescanbemitigatedwithalittlebitofattention.Non-strictevaluation,requiredforinfinitesequencesandlazyevaluation,canbeachievedwithalibrarycalledLazy.js.Immutabledatacanbeachievedsimplybyprogrammingtechnique,butthisrequiresmoreprogrammerdisciplineratherthanrelyingonthelanguagetotakecareofit.AndrecursivetailcalleliminationcanbeachievedwithamethodcalledTrampolining.TheseissueswillbeaddressedinChapter6,AdvancedTopics&PitfallsinJavaScript.

ManydebateshavebeenwagedoverwhetherornotJavaScriptisafunctionallanguage,anobject-orientedlanguage,both,orneither.Andthiswon’tbethelastdebate.

Intheend,functionalprogrammingiswayofwritingcleanercodethroughcleverwaysofmutating,combining,andusingfunctions.AndJavaScriptprovidesanexcellentmediumforthisapproach.IfyoureallywanttouseJavaScripttoitsfullpotential,youmustlearnhowtouseitasafunctionallanguage.

Workingwithfunctions Sometimes,theelegantimplementationisafunction.Notamethod.Notaclass.Notaframework.Justafunction.

—JohnCarmack,leadprogrammeroftheDoomvideogame

Functionalprogrammingisallaboutdecomposingaproblemintoasetoffunctions.Often,functionsarechainedtogether,nestedwithineachother,passedaround,andtreatedasfirst-classcitizens.Ifyou’veusedframeworkssuchasjQueryandNode.js,you’veprobablyusedsomeofthesetechniques,youjustdidn’trealizeit!

Let’sstartwithalittleJavaScriptdilemma.

Sayweneedtocompilealistofvaluesthatareassignedtogenericobjects.Theobjectscouldbeanything:dates,HTMLobjects,andsoon.

var

obj1={value:1},

obj2={value:2},

obj3={value:3};

varvalues=[];

functionaccumulate(obj){

values.push(obj.value);

}

accumulate(obj1);

accumulate(obj2);

console.log(values);//Output:[obj1.value,obj2.value]

Itworksbutit’svolatile.Anycodecanmodifythevaluesobjectwithoutcallingtheaccumulate()function.Andifweforgettoassigntheemptyset,[],tothevaluesinstancethenthecodewillnotworkatall.

Butifthevariableisdeclaredinsidethefunction,itcan’tbemutatedbyanyroguelinesofcode.

functionaccumulate2(obj){

varvalues=[];

values.push(obj.value);

returnvalues;

}

console.log(accumulate2(obj1));//Returns:[obj1.value]

console.log(accumulate2(obj2));//Returns:[obj2.value]

console.log(accumulate2(obj3));//Returns:[obj3.value]

Itdoesnotwork!Onlythevalueoftheobjectlastpassedinisreturned.

Wecouldpossiblysolvethiswithanestedfunctioninsidethefirstfunction.

varValueAccumulator=function(obj){

varvalues=[]

varaccumulate=function(){

values.push(obj.value);

};

accumulate();

returnvalues;

};

Butit’sthesameissue,andnowwecannotreachtheaccumulatefunctionorthevaluesvariable.

Whatweneedisaself-invokingfunction.

Self-invokingfunctionsandclosuresWhatifwecouldreturnafunctionexpressionthatin-turnreturnsthevaluesarray?Variablesdeclaredinafunctionareavailabletoanycodewithinthefunction,includingself-invokingfunctions.

Byusingaself-invokingfunction,ourdilemmaissolved.

varValueAccumulator=function(){

varvalues=[];

varaccumulate=function(obj){

if(obj){

values.push(obj.value);

returnvalues;

}

else{

returnvalues;

}

};

returnaccumulate;

};

//Thisallowsustodothis:

varaccumulator=ValueAccumulator();

accumulator(obj1);

accumulator(obj2);

console.log(accumulator());

//Output:[obj1.value,obj2.value]

It’sallaboutvariablescoping.Thevaluesvariableisavailabletotheinneraccumulate()function,evenwhencodeoutsidethescopecallsthefunctions.Thisiscalledaclosure.

NoteClosuresinJavaScriptarefunctionsthathaveaccesstotheparentscope,evenwhentheparentfunctionhasclosed.

Closuresareafeatureofallfunctionallanguages.Traditionalimperativelanguagesdonotallowthem.

Higher-orderfunctionsSelf-invokingfunctionsareactuallyaformofhigher-orderfunctions.Higher-orderfunctionsarefunctionsthateithertakeanotherfunctionastheinputorreturnafunctionastheoutput.

Higher-orderfunctionsarenotcommonintraditionalprogramming.Whileanimperativeprogrammermightusealooptoiterateanarray,afunctionalprogrammerwouldtakeanotherapproachentirely.Byusingahigher-orderfunction,thearraycanbeworkedonbyapplyingthatfunctiontoeachiteminthearraytocreateanewarray.

Thisisthecentralideaofthefunctionalprogrammingparadigm.Whathigher-orderfunctionsallowistheabilitytopasslogictootherfunctions,justlikeobjects.

Functionsaretreatedasfirst-classcitizensinJavaScript,adistinctionJavaScriptshareswithScheme,Haskell,andtheotherclassicfunctionallanguages.Thismaysoundbizarre,butallthisreallymeansisthatfunctionsaretreatedasprimitives,justlikenumbersandobjects.Ifnumbersandobjectscanbepassedaround,socanfunctions.

Toseethisinaction,let’suseahigher-orderfunctionwithourValueAccumulator()functionfromtheprevioussection:

//usingforEach()toiteratethroughanarrayandcalla

//callbackfunction,accumulator,foreachitem

varaccumulator2=ValueAccumulator();

varobjects=[obj1,obj2,obj3];//couldbehugearrayofobjects

objects.forEach(accumulator2);

console.log(accumulator2());

PurefunctionsPurefunctionsreturnavaluecomputedusingonlytheinputspassedtoit.Outsidevariablesandglobalstatesmaynotbeusedandtheremaybenosideeffects.Inotherwords,itmustnotmutatethevariablespassedtoitforinput.Therefore,purefunctionsareonlyusedfortheirreturnedvalue.

Asimpleexampleofthisisamathfunction.TheMath.sqrt(4)functionwillalwaysreturn2,doesnotuseanyhiddeninformationsuchassettingsorstate,andwillneverinflictanysideeffects.

Purefunctionsarethetrueinterpretationofthemathematicaltermfor‘function’,arelationbetweeninputsandanoutput.Theyaresimpletothinkaboutandarereadilyre-usable.Becausetheyaretotallyindependent,purefunctionsaremorecapableofbeingusedagainandagain.

Toillustratethis,comparethefollowingnon-purefunctiontothepureone.

//functionthatprintsamessagetothecenterofthescreen

varprintCenter=function(str){

varelem=document.createElement("div");

elem.textContent=str;

elem.style.position='absolute';

elem.style.top=window.innerHeight/2+"px";

elem.style.left=window.innerWidth/2+"px";

document.body.appendChild(elem);

};

printCenter('helloworld');

//purefunctionthataccomplishesthesamething

varprintSomewhere=function(str,height,width){

varelem=document.createElement("div");

elem.textContent=str;

elem.style.position='absolute';

elem.style.top=height;

elem.style.left=width;

returnelem;

};

document.body.appendChild(printSomewhere('helloworld',

window.innerHeight/2)+10+"px",window.innerWidth/2)+10+"px")

);

Whilethenon-purefunctionreliesonthestateofthewindowobjecttocomputetheheightandwidth,thepure,self-sufficientfunctioninsteadasksthatthosevaluesbepassedin.Whatthisactuallydoesisallowthemessagetobeprintedanywhere,andthismakesthefunctionmuchmoreversatile.

Andwhilethenon-purefunctionmayseemliketheeasieroptionbecauseitperformstheappendingitselfinsteadofreturninganelement,thepurefunctionprintSomewhere()anditsreturnedvalueplaybetterwithotherfunctionalprogrammingdesigntechniques.

varmessages=['Hi','Hello','Sup','Hey','Hola'];

messages.map(function(s,i){

returnprintSomewhere(s,100*i*10,100*i*10);

}).forEach(function(element){

document.body.appendChild(element);

});

NoteWhenthefunctionsarepureanddon’trelyonstateorenvironment,thenwedon’tcareaboutwhenorwheretheyactuallygetcomputed.We’llseethislaterwithlazyevaluation.

AnonymousfunctionsAnotherbenefitoftreatingfunctionsasfirst-classobjectsistheadventofanonymousfunctions.

Asthenamemightimply,anonymousfunctionsarefunctionswithoutnames.Buttheyaremorethanthat.Whattheyallowistheabilitytodefinead-hoclogic,on-the-spotandasneeded.Usually,it’sforthebenefitofconvenience;ifthefunctionisonlyreferredtoonce,thenavariablenamedoesn’tneedtobewastedonit.

Someexamplesofanonymousfunctionsareasfollows:

//Thestandardwaytowriteanonymousfunctions

function(){return"helloworld"};

//Anonymousfunctionassignedtovariable

varanon=function(x,y){returnx+y};

//Anonymousfunctionusedinplaceofanamedcallbackfunction,

//thisisoneofthemorecommonusesofanonymousfunctions.

setInterval(function(){console.log(newDate().getTime())},1000);

//Output:1413249010672,1413249010673,1413249010674,...

//Withoutwrappingitinananonymousfunction,itimmediately//execute

onceandthenreturnundefinedasthecallback:

setInterval(console.log(newDate().getTime()),1000)

//Output:1413249010671

Amoreinvolvedexampleofanonymousfunctionsusedwithinhigher-orderfunctions:

functionpowersOf(x){

returnfunction(y){

//thisisananonymousfunction!

returnMath.pow(x,y);

};

}

powerOfTwo=powersOf(2);

console.log(powerOfTwo(1));//2

console.log(powerOfTwo(2));//4

console.log(powerOfTwo(3));//8

powerOfThree=powersOf(3);

console.log(powerOfThree(3));//9

console.log(powerOfThree(10));//59049

Thefunctionthatisreturneddoesn’tneedtobenamed;itcan’tbeusedanywhereoutsidethepowersOf()function,andsoitisananonymousfunction.

Rememberouraccumulatorfunction?Itcanbere-writtenusinganonymousfunctions.

var

obj1={value:1},

obj2={value:2},

obj3={value:3};

varvalues=(function(){

//anonymousfunction

varvalues=[];

returnfunction(obj){

//anotheranonymousfunction!

if(obj){

values.push(obj.value);

returnvalues;

}

else{

returnvalues;

}

}

})();//makeitself-executing

console.log(values(obj1));//Returns:[obj.value]

console.log(values(obj2));//Returns:[obj.value,obj2.value]

Righton!Apure,high-order,anonymousfunction.Howdidweevergetsolucky?Actually,it’smorethanthat.It’salsoself-executingasindicatedbythestructure,(function(){...})();.Thepairofparenthesesfollowingtheanonymousfunctioncausesthefunctiontobecalledrightaway.Intheaboveexample,thevaluesinstanceisassignedtotheoutputoftheself-executingfunctioncall.

NoteAnonymousfunctionsaremorethanjustsyntacticalsugar.TheyaretheembodimentofLambdacalculus.Staywithmeonthis…Lambdacalculuswasinventedlongbeforecomputersorcomputerlanguages.Itwasjustamathematicalnotionforreasoningaboutfunctions.Remarkably,itwasdiscoveredthat—despitethefactthatitonlydefinesthreekindsofexpressions:variablereferences,functioncalls,andanonymousfunctions—itwasTuring-complete.Today,Lambdacalculusliesatthecoreofallfunctionallanguagesifyouknowhowtofindit,includingJavaScript.

Forthisreason,anonymousfunctionsareoftencalledlambdaexpressions.

Onedrawbacktoanonymousfunctionsremains.They’redifficulttoidentifyincallstacks,whichmakesdebuggingtrickier.Theyshouldbeusedsparingly.

MethodchainsChainingmethodstogetherinJavaScriptisquitcommon.Ifyou’veusedjQuery,you’velikelyperformedthistechnique.It’ssometimescalledthe“BuilderPattern”.

It’satechniquethatisusedtosimplifycodewheremultiplefunctionsareappliedtoanobjectoneafteranother.

//Insteadofapplyingthefunctionsoneperline…

arr=[1,2,3,4];

arr1=arr.reverse();

arr2=arr1.concat([5,6]);

arr3=arr2.map(Math.sqrt);

//...theycanbechainedtogetherintoaone-liner

console.log([1,2,3,4].reverse().concat([5,6]).map(Math.sqrt));

//parenthesesmaybeusedtoillustrate

console.log(((([1,2,3,4]).reverse()).concat([5,6])).map(Math.sqrt));

Thisonlyworkswhenthefunctionsaremethodsoftheobjectbeingworkedon.Ifyoucreatedyourownfunctionthat,forexample,takestwoarraysandreturnsanarraywiththetwoarrayszippedtogether,youmustdeclareitasamemberoftheArray.prototypeobject.Takealookatthefollowingcodesnippet:

Array.prototype.zip=function(arr2){

//...

}

Thiswouldallowustothefollowing:

arr.zip([11,12,13,14).map(function(n){returnn*2});

//Output:2,22,4,24,6,26,8,28

RecursionRecursionislikelythemostfamousfunctionalprogrammingtechnique.Ifyoudon’tknowbynow,arecursivefunctionisafunctionthatcallsitself.

Whenafunctionscallsitself,somethingstrangehappens.Itactsbothasaloop,inthatitexecutesthesamecodemultipletimes,andasafunctionstack.

Recursivefunctionsmustbeverycarefultoavoidaninfiniteloop(rather,infiniterecursioninthiscase).Sojustlikeloops,aconditionmustbeusedtoknowwhentostop.Thisiscalledthebasecase.

Anexampleisasfollows:

varfoo=function(n){

if(n<0){

//basecase

return'hello';

}

else{

//recursivecase

foo(n-1);

}

}

console.log(foo(5));

It’spossibletoconvertanylooptoarecursivealgorithmandanyrecursivealgorithmtoaloop.Butrecursivealgorithmsaremoreappropriate,almostnecessary,forsituationsthatdiffergreatlyfromthosewhereloopsareappropriate.

Agoodexampleistreetraversal.Whileit’snottoohardtotraverseatreeusingarecursivefunction,aloopwouldbemuchmorecomplexandwouldneedtomaintainastack.Andthatwouldgoagainstthespiritoffunctionalprogramming.

vargetLeafs=function(node){

if(node.childNodes.length==0){

//basecase

returnnode.innerText;

}

else{

//recursivecase:

returnnode.childNodes.map(getLeafs);

}

}

DivideandconquerRecursionismorethananinterestingwaytoiteratewithoutforandwhileloops.Analgorithmdesign,knownasdivideandconquer,recursivelybreaksproblemsdownintosmallerinstancesofthesameproblemuntilthey’resmallenoughtosolve.

ThehistoricalexampleofthisistheEuclidanalgorithmforfindingthegreatestcommondenominatorfortwonumbers.

functiongcd(a,b){

if(b==0){

//basecase(conquer)

returna;

}

else{

//recursivecase(divide)

returngcd(b,a%b);

}

}

console.log(gcd(12,8));

console.log(gcd(100,20));

Sointheory,divideandconquerworksquiteeloquently,butdoesithaveanyuseintherealworld?Yes!TheJavaScriptfunctionforsortingarraysisnotverygood.Notonlydoesitsortthearrayinplace,whichmeansthatthedataisnotimmutable,butitisunreliableandinflexible.Withdivideandconquer,wecandobetter.

Themergesortalgorithmusesthedivideandconquerrecursivealgorithmdesigntoefficientlysortanarraybyrecursivelydividingthearrayintosmallersub-arraysandthenmergingthemtogether.

ThefullimplementationinJavaScriptisabout40linesofcode.However,pseudo-codeisasfollows:

varmergeSort=function(arr){

if(arr.length<2){

//basecase:0or1itemarraysdon'tneedsorting

returnitems;

}

else{

//recursivecase:dividethearray,sort,thenmerge

varmiddle=Math.floor(arr.length/2);

//divide

varleft=mergeSort(arr.slice(0,middle));

varright=mergeSort(arr.slice(middle));

//conquer

//mergeisahelperfunctionthatreturnsanewarray

//ofthetwoarraysmergedtogether

returnmerge(left,right);

}

}

LazyevaluationLazyevaluation,alsoknownasnon-strictevaluation,call-by-needanddefferedexecution,isanevaluationstrategythatwaitsuntilthevalueisneededtocomputetheresultofafunctionandisparticularlyusefulforfunctionalprogramming.It’sclearthatalineofcodethatstatesx=func()iscallingforxtobeassignedtothereturnedvaluebyfunc().Butwhatxactuallyequatestodoesnotmatteruntilitisneeded.Waitingtocallfunc()untilxisneededisknownaslazyevaluation.

Thisstrategycanresultinamajorincreaseinperformance,especiallywhenusedwithmethodchainsandarrays,thefavoriteprogramflowtechniquesofthefunctionalprogrammer.

Oneexcitingbenefitoflazyevaluationistheexistenceofinfiniteseries.Becausenothingisactuallycomputeduntilitcan’tbedelayedanyfurther,it’spossibletodothis:

//wishfulJavaScriptpseudocode:

varinfinateNums=range(1toinfinity);

vartenPrimes=infinateNums.getPrimeNumbers().first(10);

Thisopensthedoorformanypossibilities:asynchronousexecution,parallelization,andcomposition,justtonameafew.

However,there’soneproblem:JavaScriptdoesnotperformLazyevaluationonitsown.Thatbeingsaid,thereexistlibrariesforJavaScriptthatsimulatelazyevaluationverywell.ThatisthesubjectofChapter3,SettingUptheFunctionalProgrammingEnvironment.

Thefunctionalprogrammer’stoolkitIfyou’velookedcloselyatthefewexamplespresentedsofar,you’llnoticeafewmethodsbeingusedthatyoumaynotbefamiliarwith.Theyarethemap(),filter(),andreduce()functions,andtheyarecrucialtoeveryfunctionalprogramofanylanguage.Theyenableyoutoremoveloopsandstatements,resultingincleanercode.

Themap(),filter(),andreduce()functionsmakeupthecoreofthefunctionalprogrammer’stoolkit,acollectionofpure,higher-orderfunctionsthataretheworkhorsesofthefunctionalmethod.Infact,they’retheepitomeofwhatapurefunctionandwhatahigher-orderfunctionshouldbelike;theytakeafunctionasinputandreturnanoutputwithzerosideeffects.

Whilethey’restandardforbrowsersthatimplementECMAScript5.1,theyonlyworkonarrays.Eachtimeit’scalled,anewarrayiscreatedandreturned.Theexistingarrayisnotmodified.Butthere’smore,theytakefunctionsasinputs,oftenintheformofanonymousfunctionsreferredtoascallbackfunctions;theyiterateoverthearrayandapplythefunctiontoeachiteminthearray!

myArray=[1,2,3,4];

newArray=myArray.map(function(x){returnx*2});

console.log(myArray);//Output:[1,2,3,4]

console.log(newArray);//Output:[2,4,6,8]

Onemorething.Becausetheyonlyworkonarrays,theydonotworkonotheriterabledatastructures,likecertainobjects.Fretnot,librariessuchasunderscore.js,Lazy.js,stream.js,andmanymoreallimplementtheirownmap(),filter(),andreduce()methodsthataremoreversatile.

CallbacksIfyou’veneverworkedwithcallbacksbefore,youmightfindtheconceptalittlepuzzling.ThisisespeciallytrueinJavaScript,giventheseveraldifferentwaysthatJavaScriptallowsyoutodeclarefunctions.

Acallback()functionisusedforpassingtootherfunctionsforthemtouse.It’sawaytopasslogicjustasyouwouldpassanobject:

varmyArray=[1,2,3];

functionmyCallback(x){returnx+1};

console.log(myArray.map(myCallback));

Tomakeitsimplerforeasytasks,anonymousfunctionscanbeused:

console.log(myArray.map(function(x){returnx+1}));

Theyarenotonlyusedinfunctionalprogramming,theyareusedformanythingsinJavaScript.Purelyforexample,here’sacallback()functionusedinanAJAXcallmadewithjQuery:

functionmyCallback(xhr){

console.log(xhr.status);

returntrue;

}

$.ajax(myURI).done(myCallback);

Noticethatonlythenameofthefunctionwasused.Andbecausewe’renotcallingthecallbackandareonlypassingthenameofit,itwouldbewrongtowritethis:

$.ajax(myURI).fail(myCallback(xhr));

//or

$.ajax(myURI).fail(myCallback());

Whatwouldhappenifwedidcallthecallback?Inthatcase,themyCallback(xhr)methodwouldtrytoexecute—‘undefined’wouldbeprintedtotheconsoleanditwouldreturnTrue.Whentheajax()callcompletes,itwillhave‘true’asthenameofthecallbackfunctiontouse,andthatwillthrowanerror.

Whatthisalsomeansisthatwecannotspecifywhatargumentsarepassedtothecallbackfunctions.Ifweneeddifferentparametersfromwhattheajax()callwillpasstoit,wecanwrapthecallbackfunctioninananonymousfunction:

functionmyCallback(status){

console.log(status);

returntrue;

}

$.ajax(myURI).done(function(xhr){myCallback(xhr.status)});

Array.prototype.map()Themap()functionistheringleaderofthebunch.Itsimplyappliesthecallbackfunctiononeachiteminthearray.

NoteSyntax:arr.map(callback[,thisArg]);

Parameters:

callback():Thisfunctionproducesanelementforthenewarray,receivingthesearguments:

currentValue:Thisargumentgivesthecurrentelementbeingprocessedinthearrayindex:Thisargumentgivestheindexofthecurrentelementinthearrayarray:Thisargumentgivesthearraybeingprocessed

thisArg():Thisfunctionisoptional.Thevalueisusedasthiswhenexecutingcallback.

Examples:

var

integers=[1,-0,9,-8,3],

numbers=[1,2,3,4],

str='helloworldhowyadoing?';

//mapintegerstotheirabsolutevalues

console.log(integers.map(Math.abs));

//multiplyanarrayofnumbersbytheirpositioninthearray

console.log(numbers.map(function(x,i){returnx*i}));

//Capitalizeeveryotherwordinastring.

console.log(str.split('').map(function(s,i){

if(i%2==0){

returns.toUpperCase();

}

else{

returns;

}

}));

NoteWhiletheArray.prototype.mapmethodisastandardmethodfortheArrayobjectinJavaScript,itcanbeeasilyextendedtoyourcustomobjectsaswell.

MyObject.prototype.map=function(f){

returnnewMyObject(f(this.value));

};

Array.prototype.filter()Thefilter()functionisusedtotakeelementsoutofanarray.ThecallbackmustreturnTrue(toincludetheiteminthenewarray)orFalse(todropit).Somethingsimilarcouldbeachievedbyusingthemap()functionandreturninganullvalueforitemsyouwantdropped,butthefilter()functionwilldeletetheitemfromthenewarrayinsteadofinsertinganullvalueinitsplace.

NoteSyntax:arr.filter(callback[,thisArg]);

Parameters:

callback():Thisfunctionisusedtotesteachelementinthearray.ReturnTruetokeeptheelement,Falseotherwise.Withtheseparameters:

currentValue:Thisparametergivesthecurrentelementbeingprocessedinthearrayindex:Thisparametergivestheindexofthecurrentelementinthearray

array:Thisparametergivesthearraybeingprocessed.thisArg():Thisfunctionisoptional.Valueisusedasthiswhenexecutingcallback.

Examples:

varmyarray=[1,2,3,4]

words='hello123worldhow345yadoing'.split('');

re='[a-zA-Z]';

//removeallnegativenumbers

console.log([-2,-1,0,1,2].filter(function(x){returnx>0}));

//removenullvaluesafteramapoperation

console.log(words.filter(function(s){

returns.match(re);

}));

//removerandomobjectsfromanarray

console.log(myarray.filter(function(){

returnMath.floor(Math.random()*2)})

);

Array.prototype.reduce()Sometimescalledfold,thereduce()functionisusedtoaccumulateallthevaluesofthearrayintoone.Thecallbackneedstoreturnthelogictobeperformedtocombinetheobjects.Inthecaseofnumbers,they’reusuallyaddedtogethertogetasumormultipliedtogethertogetaproduct.Inthecaseofstrings,thestringsareoftenappendedtogether.

NoteSyntax:arr.reduce(callback[,initialValue]);

Parameters:

callback():Thisfunctioncombinestwoobjectsintoone,whichisreturned.Withtheseparameters:

previousValue:Thisparametergivesthevaluepreviouslyreturnedfromthelastinvocationofthecallback,ortheinitialValue,ifsuppliedcurrentValue:Thisparametergivesthecurrentelementbeingprocessedinthearrayindex:Thisparametergivestheindexofthecurrentelementinthearrayarray:Thisparametergivesthearraybeingprocessed

initialValue():Thisfunctionisoptional.Objecttouseasthefirstargumenttothefirstcallofthecallback.

Examples:

varnumbers=[1,2,3,4];

//sumupallthevaluesofanarray

console.log([1,2,3,4,5].reduce(function(x,y){returnx+y},0));

//sumupallthevaluesofanarray

console.log([1,2,3,4,5].reduce(function(x,y){returnx+y},0));

//findthelargestnumber

console.log(numbers.reduce(function(a,b){

returnMath.max(a,b)})//maxtakestwoarguments

);

HonorablementionsThemap(),filter(),andreduce()functionsarenotaloneinourtoolboxofhelperfunctions.Thereexistmanymorefunctionsthatcanbepluggedintonearlyanyfunctionalapplication.

Array.prototype.forEachEssentiallythenon-pureversionofmap(),forEach()iteratesoveranarrayandappliesacallback()functionovereachitem.However,itdoesn’treturnanything.It’sacleanerwayofperformingaforloop.

NoteSyntax:arr.forEach(callback[,thisArg]);

Parameters:

callback():Thisfunctionistobeperformedforeachvalueofthearray.Withtheseparameters:

currentValue:Thisparametergivesthecurrentelementbeingprocessedinthearrayindex:Thisparametergivestheindexofthecurrentelementinthearrayarray:Thisparametergivesthearraybeingprocessed

thisArg:Thisfunctionisoptional.Valueisusedasthiswhenexecutingcallback.

Examples:

vararr=[1,2,3];

varnodes=arr.map(function(x){

varelem=document.createElement("div");

elem.textContent=x;

returnelem;

});

//logthevalueofeachitem

arr.forEach(function(x){console.log(x)});

//appendnodestotheDOM

nodes.forEach(function(x){document.body.appendChild(x)});

Array.prototype.concatWhenworkingwitharraysinsteadofforandwhileloops,oftenyouwillneedtojoinmultiplearraystogether.Anotherbuilt-inJavaScriptfunction,concat(),takescareofthisforus.Theconcat()functionreturnsanewarrayandleavestheoldarraysuntouched.Itcanjoinasmanyarraysasyoupasstoit.

console.log([1,2,3].concat(['a','b','c'])//concatenatetwoarrays);

//Output:[1,2,3,'a','b','c']

Theoriginalarrayisuntouched.Itreturnsanewarraywithbotharraysconcatenated

together.Thisalsomeansthattheconcat()functioncanbechainedtogether.

vararr1=[1,2,3];

vararr2=[4,5,6];

vararr3=[7,8,9];

varx=arr1.concat(arr2,arr3);

vary=arr1.concat(arr2).concat(arr3));

varz=arr1.concat(arr2.concat(arr3)));

console.log(x);

console.log(y);

console.log(z);

Variablesx,yandzallcontain[1,2,3,4,5,6,7,8,9].

Array.prototype.reverseAnothernativeJavaScriptfunctionhelpswitharraytransformations.Thereverse()functioninvertsanarray,suchthatthefirstelementisnowthelastandthelastisnowthefirst.

However,itdoesnotreturnanewarray;insteaditmutatesthearrayinplace.Wecandobetter.Here’sanimplementationofapuremethodforreversinganarray:

varinvert=function(arr){

returnarr.map(function(x,i,a){

returna[a.length-(i+1)];

});

};

varq=invert([1,2,3,4]);

console.log(q);

Array.prototype.sortMuchlikeourmap(),filter(),andreduce()methods,thesort()methodtakesacallback()functionthatdefineshowtheobjectswithinanarrayshouldbesorted.But,likethereverse()function,itmutatesthearrayinplace.Andthat’snobueno.

arr=[200,12,56,7,344];

console.log(arr.sort(function(a,b){returna–b}));

//arrisnow:[7,12,56,200,344];

Wecouldwriteapuresort()functionthatdoesn’tmutatethearray,butsortingalgorithmsisthesourceofmuchgrief.Significantlylargearraysthatneedtobesortedreallyshouldbeorganizedindatastructuresthataredesignedjustforthat:quickStort,mergeSort,bubbleSort,andsoon.

Array.prototype.everyandArray.prototype.someTheArray.prototype.every()andArray.prototype.some()functionsarebothpureandhigh-orderfunctionsthataremethodsoftheArrayobjectandareusedtotesttheelementsofanarrayagainstacallback()functionthatmustreturnaBooleanrepresentingtherespectiveinput.Theevery()functionreturnsTrueifthecallback()functionreturnsTrueforeveryelementinthearray,andthesome()functionreturnsTrueifsomeelementsinthearrayareTrue.

Example:

functionisNumber(n){

return!isNaN(parseFloat(n))&&isFinite(n);

}

console.log([1,2,3,4].every(isNumber));//Return:true

console.log([1,2,'a'].every(isNumber));//Return:false

console.log([1,2,'a'].some(isNumber));//Return:true

SummaryInordertodevelopanunderstandingoffunctionalprogramming,thischaptercoveredafairlybroadrangeoftopics.Firstweanalyzedwhatitmeansforaprogramminglanguagetobefunctional,thenweevaluatedJavaScriptforitsfunctionalprogrammingcapabilities.Next,weappliedthecoreconceptsoffunctionalprogrammingusingJavaScriptandshowcasedsomeofJavaScript’sbuilt-infunctionsforfunctionalprogramming.

AlthoughJavaScriptdoeshaveafewtoolsforfunctionalprogramming,itsfunctionalcoreremainsmostlyhiddenandmuchistobedesired.Inthenextchapter,wewillexploreseverallibrariesforJavaScriptthatexposeitsfunctionalunderbelly.

Chapter3.SettingUptheFunctionalProgrammingEnvironment

IntroductionDoweneedtoknowadvancedmath—categorytheory,Lambdacalculus,polymorphisms—justtowriteapplicationswithfunctionalprogramming?Doweneedtoreinventthewheel?Theshortanswertoboththesequestionsisno.

Inthischapter,wewilldoourbesttosurveyeverythingthatcanimpactthewaywewriteourfunctionalapplicationsinJavaScript.

LibrariesToolkitsDevelopmentenvironmentsFunctionallanguagethatcompilestoJavaScriptAndmore

PleaseunderstandthatthecurrentlandscapeoffunctionallibrariesforJavaScriptisaveryfluidone.Likeallaspectsofcomputerprogramming,thecommunitycanchangeinaheartbeat;newlibrariescanbeadoptedandoldonescanbeabandoned.Forinstance,duringthewritingprocessofthisverybook,thepopularandstableNode.jsplatformforI/Ohasbeenforkedbyitsopensourcecommunity.Itsfutureisvague.

Therefore,themostimportantconcepttobegainedfromthischapterisnothowtousethecurrentlibrariesforfunctionalprogramming,buthowtouseanylibrarythatenhancesJavaScript’sfunctionalprogrammingmethod.Thischapterwillnotfocusonjustoneortwolibraries,butwillexploreasmanyaspossiblewiththegoalofsurveyingallthemanystylesoffunctionalprogrammingthatexistwithinJavaScript.

FunctionallibrariesforJavaScriptIt’sbeensaidthateveryfunctionalprogrammerwritestheirownlibraryoffunctions,andfunctionalJavaScriptprogrammersarenoexception.Withtoday’sopensourcecode-sharingplatformssuchasGitHub,Bower,andNPM,it’seasiertoshare,collaborate,andgrowtheselibraries.ManylibrariesexistforfunctionalprogrammingwithJavaScript,rangingfromtinytoolkitstomonolithicmodulelibraries.

Eachlibrarypromotesitsownstyleoffunctionalprogramming.Fromarigid,math-basedstyletoarelaxed,informalstyle,eachlibraryisdifferentbuttheyallshareonecommonfeature:theyallhaveabstractJavaScriptfunctionalcapabilitiestoincreasecodere-use,readability,androbustness.

Atthetimeofwriting,however,asinglelibraryhasnotestablisheditselfasthede-factostandard.Somemightarguethatunderscore.jsistheonebut,asyou’llseeinthefollowingsection,itmightbeadvisabletoavoidunderscore.js.

Underscore.jsUnderscorehasbecomethestandardfunctionalJavaScriptlibraryintheeyesofmany.Itismature,stable,andwascreatedbyJeremyAshkenas,themanbehindtheBackbone.jsandCoffeeScriptlibraries.UnderscoreisactuallyareimplementationofRuby’sEnumerablemodule,whichexplainswhyCoffeeScriptwasalsoinfluencedbyRuby.

SimilartojQuery,Underscoredoesn’tmodifynativeJavaScriptobjectsandinsteadusesasymboltodefineitsownobject:theunderscorecharacter“_“.So,usingUnderscorewouldworklikethis:

varx=_.map([1,2,3],Math.sqrt);//Underscore'smapfunction

console.log(x.toString());

We’vealreadyseenJavaScrip’snativemap()methodfortheArrayobject,whichworkslikethis:

varx=[1,2,3].map(Math.sqrt);

Thedifferenceisthat,inUnderscore,boththeArrayobjectandthecallback()functionarepassedasparameterstotheUnderscoreobject’smap()method(_.map),asopposedtopassingonlythecallbacktothearray’snativemap()method(Array.prototype.map).

Butthere’swaymorethanjustmap()andotherbuilt-infunctionstoUnderscore.It’sfullofsuperhandyfunctionssuchasfind(),invoke(),pluck(),sortyBy(),groupBy(),andmore.

vargreetings=[{origin:'spanish',value:'hola'},

{origin:'english',value:'hello'}];

console.log(_.pluck(greetings,'value'));

//Grabsanobject'sproperty.

//Returns:['hola','hello']

console.log(_.find(greetings,function(s){returns.origin==

'spanish';}));

//Looksforthefirstobjthatpassesthetruthtest

//Returns:{origin:'spanish',value:'hola'}

greetings=greetings.concat(_.object(['origin','value'],

['french','bonjour']));

console.log(greetings);

//_.objectcreatesanobjectliteralfromtwomergedarrays

//Returns:[{origin:'spanish',value:'hola'},

//{origin:'english',value:'hello'},

//{origin:'french',value:'bonjour'}]

Anditprovidesawayofchainingmethodstogether:

varg=_.chain(greetings)

.sortBy(function(x){returnx.value.length})

.pluck('origin')

.map(function(x){returnx.charAt(0).toUpperCase()+x.slice(1)})

.reduce(function(x,y){returnx+''+y},'')

.value();

//Appliesthefunctions

//Returns:'SpanishEnglishFrench'

console.log(g);

NoteThe_.chain()methodreturnsawrappedobjectthatholdsalltheUnderscorefunctions.The_.valuemethodisthenusedtoextractthevalueofthewrappedobject.WrappedobjectsarealsoveryusefulformixingUnderscorewithobject-orientedprogramming.

Despiteitseaseofuseandadaptationbythecommunity,theunderscore.jslibraryhasbeencriticizedforforcingyoutowriteoverlyverbosecodeandforencouragingthewrongpatterns.Underscore’sstructuremaynotbeidealorevenfunction!

Untilversion1.7.0,releasedshortlyafterBrianLonsdorf’stalkentitledHeyUnderscore,you’redoingitwrong!,landedonYouTube,Underscoreexplicitlypreventedusfromextendingfunctionssuchasmap(),reduce(),filter(),andmore.

_.prototype.map=function(obj,iterate,[context]){

if(Array.prototype.map&&obj.map===Array.prototype.map)return

obj.map(iterate,context);

//...

};

NoteYoucanwatchthevideoofBrianLonsdorf’stalkatwww.youtube.com/watch?v=m3svKOdZij.

Map,intermsofcategorytheory,isahomomorphicfunctorinterface(moreonthisinChapter5,CategoryTheory).Andweshouldbeabletodefinemapasafunctorforwhateverweneeditfor.Sothat’snotveryfunctionalofUnderscore.

AndbecauseJavaScriptdoesn’thavebuilt-inimmutabledata,afunctionallibraryshouldbecarefultonotallowitshelperfunctionstomutatetheobjectspassedtoit.Agoodexampleofthisproblemisshownbelow.Theintentionofthesnippetistoreturnanewselectedlistwithoneoptionsetasthedefault.Butwhatactuallyhappensisthattheselectedlistismutatedinplace.

functiongetSelectedOptions(id,value){

options=document.querySelectorAll('#'+id+'option');

varnewOptions=_.map(options,function(opt){

if(opt.text==value){

opt.selected=true;

opt.text+='(thisisthedefault)';

}

else{

opt.selected=false;

}

returnopt;

});

returnnewOptions;

}

varoptionsHelp=getSelectedOptions('timezones','Chicago');

Wewouldhavetoinsertthelineopt=opt.cloneNode();tothecallback()functionto

makeacopyofeachobjectwithinthelistbeingpassedtothefunction.Underscore’smap()functioncheatstoboostperformance,butitisatthecostoffunctionalfengshui.ThenativeArray.prototype.map()functionwouldn’trequirethisbecauseitmakesacopy,butitalsodoesn’tworkonnodelistcollections.

Underscoremaybelessthanidealformathematically-correct,functionalprogramming,butitwasneverintendedtoextendortransformJavaScriptintoapurefunctionallanguage.ItdefinesitselfasaJavaScriptlibrarythatprovidesawholemessofusefulfunctionalprogramminghelpers.Itmaybealittlemorethanaspuriouscollectionoffunctional-likehelpers,butit’snoseriousfunctionallibraryeither.

Isthereabetterlibraryoutthere?Perhapsonethatisbasedonmathematics?

FantasyLandSometimes,thetruthisstrangerthanfiction.

FantasyLandisacollectionoffunctionalbaselibrariesandaformalspecificationforhowtoimplement“algebraicstructures”inJavaScript.Morespecifically,FantasyLandspecifiestheinteroperabilityofcommonalgebraicstructures,oralgebrasforshort:monads,monoids,setoids,functors,chains,andmore.Theirnamesmaysoundscary,butthey’rejustasetofvalues,asetofoperators,andsomelawsitmustobey.Inotherwords,they’rejustobjects.

Here’showitworks.EachalgebraisaseparateFantasyLandspecificationandmayhavedependenciesonotheralgebrasthatneedtobeimplemented.

Someofthealgebraspecificationsare:

Setoids:

Implementthereflexivity,symmetryandtransitivitylawsDefinetheequals()method

Semigroups

ImplementtheassociativitylawDefinetheconcat()method

Monoid

ImplementrightidentityandleftidentityDefinetheempty()method

Functor

ImplementtheidentityandcompositionlawsDefinethemap()method

Thelistgoesonandon.

Wedon’tnecessarilyneedtoknowexactlywhateachalgebraisforbutitcertainlyhelps,

especiallyifyou’rewritingyourownlibrarythatconformstothespecifications.It’snotjustabstractnonsense,itoutlinesameansofimplementingahigh-levelabstractioncalledcategorytheory.AfullexplanationofcategorytheorycanbefoundinChapter5,CategoryTheory.

FantasyLanddoesn’tjusttellushowtoimplementfunctionalprogramming,itdoesprovideasetoffunctionalmodulesforJavaScript.However,manyareincompleteanddocumentationisprettysparse.ButFantasyLandisn’ttheonlylibraryouttheretoimplementitsopensourcespecifications.Othershavetoo,namely:Bilby.js.

Bilby.jsWhattheheckisabilby?No,it’snotamythicalcreaturethatmightexistinFantasyLand.ItexistshereonEarthasafreaky/cutecrossbetweenamouseandarabbit.Nonetheless,bibly.jslibraryiscompliantwithFantasyLandspecifications.

Infact,bilby.jsisaseriousfunctionallibrary.Asitsdocumentationstates,itis,Serious,meaningitappliescategorytheorytoenablehighlyabstractcode.Functional,meaningitenablesreferentiallytransparentprograms.Wow,thatisprettyserious.Thedocumentationlocatedathttp://bilby.brianmckenna.org/goesontosaythatitprovides:

Immutablemulti-methodsforad-hocpolymorphismFunctionaldatastructuresOperatoroverloadingforfunctionalsyntaxAutomatedspecificationtesting(ScalaCheck,QuickCheck)

ByfarthemostmaturelibrarythatconformstotheFantasyLandspecificationsforalgebraicstructures,Bilby.jsisagreatresourceforfullycommittingtothefunctionalstyle.

Let’stryanexample:

//environmentsinbilbyareimmutablestructureformultimethods

varshapes1=bilby.environment()

//candefinemethods

.method(

'area',//methodstakeaname

function(a){returntypeof(a)=='rect'},//apredicate

function(a){returna.x*a.y}//andanimplementation

)

//andproperties,likemethodswithpredicatesthatalways

//returntrue

.property(

'name',//takesaname

'shape');//andafunction

//nowwecanoverloadit

varshapes2=shapes1

.method(

'area',function(a){returntypeof(a)=='circle'},

function(a){returna.r*a.r*Math.PI});

varshapes3=shapes2

.method(

'area',function(a){returntypeof(a)=='triangle'},

function(a){returna.height*a.base/2});

//andnowwecandosomethinglikethis

varobjs=[{type:'circle',r:5},{type:'rect',x:2,y:3}];

varareas=objs.map(shapes3.area);

//andthis

vartotalArea=objs.map(shapes3.area).reduce(add);

Thisiscategorytheoryandad-hocpolymorphisminaction.Again,categorytheorywillbe

coveredinfullinChapter5,CategoryTheory.

NoteCategorytheoryisarecentlyinvigoratedbranchofmathematicsthatfunctionalprogrammersusetomaximizetheabstractionandusefulnessoftheircode.Butthereisamajordrawback:it’sdifficulttoconceptualizeandquicklygetstartedwith.

ThetruthisthatBilbyandFantasyLandarereallystretchingthepossibilitiesoffunctionalprogramminginJavaScript.Althoughit’sexcitingtoseetheevolutionofcomputerscience,theworldmayjustnotbereadyforthekindofhard-corefunctionalstylethatBiblyandFantasyLandarepushing.

Maybesuchagrandioselibraryonthebleeding-edgeoffunctionalJavaScriptisnotourthing.Afterall,wesetouttoexplorethefunctionaltechniquesthatcomplementJavaScript,nottobuildfunctionalprogrammingdogma.Let’sturnourattentiontoanothernewlibrary,Lazy.js.

Lazy.jsLazyisautilitylibrarymorealongthelinesoftheunderscore.jslibrarybutwithalazyevaluationstrategy.Becauseofthis,Lazymakestheimpossiblepossiblebyfunctionallycomputingresultsofseriesthatwon’tbeavailablewithimmediateinterpretation.Italsoboastsasignificantperformanceboost.

TheLazy.jslibraryisstillveryyoung.Butithasalotofmomentumandcommunityenthusiasmbehindit.

Theideaisthat,inLazy,everythingisasequencethatwecaniterateover.Owingtothewaythelibrarycontrolstheorderinwhichmethodsareapplied,manyreallycoolthingscanbeachieved:asynchronousiteration(parallelprogramming),infinitesequences,functionalreactiveprogramming,andmore.

Thefollowingexamplesshowoffabitofeverything:

//Getthefirsteightlinesofasong'slyrics

varlyrics="Loremipsumdolorsitamet,consecteturadipiscingeli

//WithoutLazy,theentirestringisfirstsplitintolines

console.log(lyrics.split('\n').slice(0,3));

//WithLazy,thetextisonlysplitintothefirst8lines

//Thelyricscanevenbeinfinitelylong!

console.log(Lazy(lyrics).split('\n').take(3));

//First10squaresthatareevenlydivisibleby3

varoneTo1000=Lazy.range(1,1000).toArray();

varsequence=Lazy(oneTo1000)

.map(function(x){returnx*x;})

.filter(function(x){returnx%3===0;})

.take(10)

.each(function(x){console.log(x);});

//asynchronousiterationoveraninfinitesequence

varasyncSequence=Lazy.generate(function(x){returnx++})

.async(100)//0.100sintervalsbetweenelements

.take(20)//onlycomputethefirst20

.each(function(e){//beginiteratingoverthesequence

console.log(newDate().getMilliseconds()+":"+e);

});

Moreexamplesanduse-casesarecoveredinChapter4,ImplementingFunctionalProgrammingTechniquesinJavaScript.

ButitsnotentirelycorrecttofullycredittheLazy.jslibrarywiththisidea.Oneofitspredecessors,theBacon.jslibrary,worksinmuchthesameway.

Bacon.jsThelogoofBacon.jslibraryisasfollows:

Themustachioedhipsteroffunctionalprogramminglibraries,Bacon.jsisitselfalibraryforfunctionalreactiveprogramming.Functionalreactiveprogrammingjustmeansthatfunctionaldesignpatternsareusedtorepresentvaluesthatarereactiveandalwayschanging,likethepositionofthemouseonthescreen,orthepriceofacompany’sstock.InthesamewaythatLazycangetawaywithcreatinginfinitesequencesbynotcalculatingthevalueuntilit’sneeded,Baconcanavoidhavingtocalculateever-changingvaluesuntiltheverylastsecond.

WhatarecalledsequencesinLazyareknownasEventStreamsandPropertiesinBaconbecausethey’remoresuitedforworkingwithevents(onmouseover,onkeydown,andsoon)andreactiveproperties(scrollposition,mouseposition,toggles,andsoon).

Bacon.fromEventTarget(document.body,"click")

.onValue(function(){alert("Bacon!")});

BaconisalittlebitolderthanLazybutitsfeaturesetisabouthalfthesizeanditscommunityenthusiasmisaboutequal.

HonorablementionsTherearesimplytoomanylibrariesouttheretodothemalljusticewithinthescopeofthisbook.Let’slookatafewmorelibrariesforfunctionalprogramminginJavaScript.

Functional

PossiblythefirstlibraryforfunctionalprogramminginJavaScript,Functionalisalibrarythatincludescomprehensivehigher-orderfunctionsupportaswellasstringlambdas

wu.js

Especiallyprizedforitscurryable()function,wu.jslibraryisaveryniceLibraryforfunctionalprogramming.Itwasthefirstlibrary(thatIknowof)toimplementlazyevaluation,gettingtheballrollingforBacon.js,Lazy.jsandotherlibrariesYes,itisnamedaftertheinfamousrapgroupWuTangClan

sloth.js

VerysimilartotheLazy.jslibraries,butmuchsmaller

stream.js

Thestream.jslibrarysupportsinfinitestreamsandnotmuchelseAbsolutelytinyinsize

Lo-Dash.js

Asthenamemightimply,thelo-dash.jslibrarywasinspiredbytheunderscore.jslibraryHighlyoptimized

Sugar

SugarisasupportlibraryforfunctionalprogrammingtechniquesinJavaScript,likeUnderscore,butwithsomekeydifferencesinhowit’simplemented.Insteadofdoing_.pluck(myObjs,'value')inUnderscore,it’sjustmyObjs.map('value')inSugar.ThismeansthatitmodifiesnativeJavaScriptobjects,sothereisasmallriskofitnotplayingnicelywithotherlibrariesthatdothesamesuchasPrototype.Verygooddocumentation,unittests,analyzers,andmore.

from.js

AnewfunctionallibraryandLINQ(LanguageIntegratedQuery)engineforJavaScriptthatsupportsmostofthesameLINQfunctionsthat.NETprovides100%lazyevaluationandsupportslambdaexpressionsVeryyoungbutdocumentationisexcellent

JSLINQ

AnotherfunctionalLINQengineforJavaScriptMucholderandmorematurethanfrom.jslibrary

Boiler.js

AnotherutilitylibrarythatextendsJavaScript’sfunctionalmethodstomoreprimitives:strings,numbers,objects,collectionsandarrays

Folktale

LiketheBilby.jslibrary,FolktaleisanothernewlibrarythatimplementstheFantasyLandspecifications.Andlikeitsforefather,FolktaleisalsoacollectionoflibrariesforfunctionalprogramminginJavaScript.It’sveryyoungbutcouldhaveabrightfuture.

jQuery

SurprisedtoseejQuerymentionedhere?AlthoughjQueryisnotatoolusedtoperformfunctionalprogramming,itneverthelessisfunctionalitself.jQuerymightbeoneofthemostwidelyusedlibrariesthathasitsrootsinfunctionalprogramming.ThejQueryobjectisactuallyamonad.jQueryusesthemonadiclawstoenablemethodchaining:

$('#mydiv').fadeIn().css('left':50).alert('hi!');

AfullexplanationofthiscanbefoundinChapter7,FunctionalandObject-orientedProgramminginJavaScript.

Andsomeofitsmethodsarehigher-order:

$('li').css('left':function(index){returnindex*50});

AsofjQuery1.8,thedeferred.thenparameterimplementsafunctionalconceptknownasPromises.jQueryisanabstractionlayer,mainlyfortheDOM.It’snotaframeworkoratoolkit,justawaytouseabstractiontoincreasecode-reuseandreduceuglycode.Andisn’tthatwhatfunctionalprogrammingisallabout?

DevelopmentandproductionenvironmentsItdoesnotmatterintermsofprogrammingstylewhattypeofenvironmenttheapplicationisbeingdevelopedinandwillbedeployedin.Butitdoesmattertothelibrariesalot.

BrowsersThemajorityofJavaScriptapplicationsaredesignedtorunontheclientside,thatis,intheclient’sbrowser.Browser-basedenvironmentsareexcellentfordevelopmentbecausebrowsersareubiquitous,youcanworkonthecoderightonyourlocalmachine,theinterpreteristhebrowser’sJavaScriptengine,andallbrowsershaveadeveloperconsole.Firefox’sFireBugprovidesveryusefulerrormessagesandallowsforbreak-pointsandmore,butit’softenhelpfultorunthesamecodeinChromeandSafaritocross-referencetheerroroutput.EvenInternetExplorercontainsdevelopertools.

TheproblemwithbrowsersisthattheyevaluateJavaScriptdifferently!Thoughit’snotcommon,itispossibletowritecodethatreturnsverydifferentresultsindifferentbrowsers.Butusuallythedifferencesareinthewaytheytreatthedocumentobjectmodelandnothowprototypesandfunctionswork.Obviously,Math.sqrt(4)methodreturns2toallbrowsersandshells.ButthescrollLeftmethoddependsonthebrowser’slayoutpolicies.

Writingbrowser-specificcodeisawasteoftime,andthat’sanotherreasonwhylibrariesshouldbeused.

Server-sideJavaScriptTheNode.jslibraryhasbecomethestandardplatformforcreatingserver-sideandnetwork-basedapplications.Canfunctionalprogrammingbeusedforserver-sideapplicationprogramming?Yes!Ok,butdothereexistanyfunctionallibrariesthataredesignedforthisperformance-criticalenvironment?Theanswertothatisalso:yes.

AllthefunctionallibrariesoutlinedinthischapterwillworkintheNode.jslibrary,andmanydependonthebrowserify.jsmoduletoworkwithbrowserelements.

Afunctionalusecaseintheserver-sideenvironmentInourbravenewworldofnetworksystems,server-sideapplicationdevelopersareoftenconcernedwithconcurrency,andrightlyso.Theclassicexampleisanapplicationthatallowsmultipleuserstomodifythesamefile.Butiftheytrytomodifyitatthesametime,youwillgetintoanuglymess.Thisisthemaintenanceofstateproblemthathasplaguedprogrammersfordecades.

Assumethefollowingscenario:

1. Onemorning,Adamopensareportforeditingbuthedoesn’tsaveitbeforeleavingforlunch.

2. Billyopensthesamereport,addshisnotes,andthensavesit.3. Adamcomesbackfromlunch,addshisnotestothereport,andthensavesit,

unknowinglyoverwritingBilly’snotes.4. Thenextday,Billyfindsoutthathisnotesaremissing.Hisbossyellsathim;

everybodygetsmadandtheyganguponthemisguidedapplicationdeveloperwhounfairlyloseshisjob.

Foralongtime,thesolutiontothisproblemwastocreateastateaboutthefile.Togglealockstatustoonwhensomeonebeginseditingit,whichpreventsothersfrombeingabletoeditit,andthentoggleittooffoncetheysaveit.Inourscenario,BillywouldnotbeabletodohisworkuntilAdamgetsbackfromlunch.Andifit’sneversaved(if,say,Adamdecidedtoquithisjobinthemiddleofthelunchbreak),thennoonewilleverbeabletoeditit.

Thisiswherefunctionalprogramming’sideasaboutimmutabledataandstate(orlackthereof)canreallybeputtowork.Insteadofhavingusersmodifythefiledirectly,withafunctionalapproachtheywouldmodifyacopyofthefile,whichisanewrevision.Iftheygotosavetherevisionandanewrevisionalreadyexists,thenweknowthatsomeoneelsehasalreadymodifiedtheoldone.Crisisaverted.

Nowthescenariofrombeforewouldunfoldlikethis:

1. Onemorning,Adamopensareportforediting.Buthedoesn’tsaveitbeforegoingtolunch.

2. Billyopensthesamereport,addshisnotes,andsavesitasanewrevision.3. Adamreturnsfromlunchtoaddhisnotes.Whenheattemptstosavethenew

revision,theapplicationtellshimthatanewerrevisionnowexists.4. Adamopensthenewrevisions,addshisnotestoit,andsavesanothernewrevision.5. Bylookingattherevisionhistory,thebossseesthateverythingisworkingsmoothly.

Everyoneishappyandtheapplicationdevelopergetsapromotionandaraise.

Thisisknownaseventsourcing.Thereisnoexplicitstatetobemaintained,onlyevents.Theprocessismuchcleanerandthereisaclearhistoryofeventsthatcanbereviewed.

Thisideaandmanyothersarewhyfunctionalprogramminginserver-sideenvironmentsisontherise.

CLIAlthoughwebandthenode.jslibraryarethetwomainJavaScriptenvironments,somepragmaticandadventuroususersarefindingwaystouseJavaScriptinthecommandline.

UsingJavaScriptasaCommandLineInterface(CLI)scriptinglanguagemightbeoneofthebestopportunitiestoapplyfunctionprogramming.ImaginebeingabletouselazyevaluationwhensearchingforlocalfilesortorewriteanentirebashscriptintoafunctionalJavaScriptone-liner.

UsingfunctionallibrarieswithotherJavaScriptmodulesWebapplicationsaremadeupofallsortsofthings:frameworks,libraries,APIsandmore.Theycanworkalongsideeachotherasdependents,plugins,orjustascoexistingobjects.

Backbone.js

AnMVP(model-view-provider)frameworkwithaRESTfulJSONinterfaceRequirestheunderscore.jslibrary,Backbone’sonlyharddependency

jQuery

TheBacon.jslibraryhasbindingsformixingwithjQueryUnderscoreandjQuerycomplementeachotherverywell

PrototypeJavaScriptFramework

ProvidesJavaScriptwithcollectionfunctionsinthemannerclosesttoRuby’sEnumerable

Sugar.js

ModifiesnativeobjectsandtheirmethodsMustbecarefulwhenmixingwithotherlibraries,especiallyPrototype

FunctionallanguagesthatcompileintoJavaScriptSometimesthethickveneerofC-likesyntaxoverJavaScript’sinnerfunctionalitycanbeenoughtomakeyouwanttoswitchtoanotherfunctionallanguage.Well,youcan!

ClojureandClojureScript

ClosureisamodernLispimplementationandafull-featuredfunctionallanguageClojureScripttrans-compilesClojureintoJavaScript

CoffeeScript

CoffeeScriptisthenameofbothafunctionallanguageandacompilerfortrans-compilingthelanguageintoJavaScript1-to-1mappingbetweenexpressionsinCoffeeScriptandexpressioninJavaScript

Therearemanymoreoutthere,includingPyjs,Roy,TypeScript,UHCandmore.

SummaryWhichlibraryyouchoosetousedependsonwhatyourneedsare.Needfunctionalreactiveprogrammingtohandleeventsanddynamicvalues?UsetheBacon.jslibrary.Onlyneedinfinitestreamsandnothingelse?Usethestream.jslibrary.WanttocomplementjQuerywithfunctionalhelpers?Trytheunderscore.jslibrary.Needastructuredenvironmentforseriousadhocpolymorphism?Checkoutthebilby.jslibrary.Needawell-roundedtoolforfunctionalprogramming?UsetheLazy.jslibrary.Nothappywithanyoftheseoptions?Writeyourown!

Anylibraryisonlyasgoodasthewayit’sused.Althoughafewofthelibrariesoutlinedinthischapterhaveafewflaws,mostfaultsoccursomewherebetweenthekeyboardandthechair.It’suptoyoutousethelibrariescorrectlyandtosuityourneeds.

Andifwe’reimportingcodelibrariesintoourJavaScriptenvironment,thenmaybewecanimportideasandprinciplestoo.MaybewecanchannelTheZenofPython,byTimPeter:

Beautifulisbetterthanugly

Explicitisbetterthanimplicit.

Simpleisbetterthancomplex.

Complexisbetterthancomplicated.

Flatisbetterthannested.

Sparseisbetterthandense.

Readabilitycounts.

Specialcasesaren’tspecialenoughtobreaktherules.

Althoughpracticalitybeatspurity.

Errorsshouldneverpasssilently.

Unlessexplicitlysilenced.

Inthefaceofambiguity,refusethetemptationtoguess.

Thereshouldbeone—andpreferablyonlyone—obviouswaytodoit.

Althoughthatwaymaynotbeobviousatfirstunlessyou’reDutch.

Nowisbetterthannever.

Althoughneverisoftenbetterthan“right”now.

Iftheimplementationishardtoexplain,it’sabadidea.

Iftheimplementationiseasytoexplain,itmaybeagoodidea.

Namespacesareonehonkinggreatidea—let’sdomoreofthose!

Chapter4.ImplementingFunctionalProgrammingTechniquesinJavaScriptHoldontoyourhatsbecausewe’rereallygoingtogetintothefunctionalmind-setnow.

Inthischapter,we’regoingtodothefollowing:

PutallthecoreconceptstogetherintoacohesiveparadigmExplorethebeautythatfunctionalprogramminghastoofferwhenwefullycommittothestyleStepthroughthelogicalprogressionoffunctionalpatternsastheybuilduponeachotherAllthewhile,wewillbuildupasimpleapplicationthatdoessomeprettycoolstuff

YoumayhavenoticedafewconceptsthatwerebroughtupinthelastchapterwhendealingwithfunctionallibrariesforJavaScript,butnotinChapter2,FundamentalsofFunctionalProgramming.Well,thatwasforareason!Compositions,currying,partialapplication,andmore.Let’sexplorewhyandhowtheselibrariesimplementedthoseconcepts.

Functionalprogrammingcancomeinavarietyofflavorsandpatterns.Thischapterwillcovermanydifferentstylesoffunctionalprogramming:

DatagenericprogrammingMostlyfunctionalprogrammingFunctionalreactiveprogrammingandmore

Thischapter,however,willbeasstyle-unbiasedaspossible.Withoutleaningtoohardononestyleoffunctionalprogrammingoveranother,theoverallgoalistoshowthattherearebetterwaystowritecodethanwhatisoftenacceptedasthecorrectandonlyway.Onceyoufreeyourmindaboutthepreconceptionsofwhatistherightwayandwhatisnottherightwaytowritecode,youcandowhateveryouwant.Whenyoujustwritecodewithchildlikeabandonfornoreasonotherthanthefactthatyoulikeitandwhenyou’renotconcernedaboutconformingtothetraditionalwayofdoingthings,thenthepossibilitiesareendless.

PartialfunctionapplicationandcurryingManylanguagessupportoptionalarguments,butnotinJavaScript.JavaScriptusesadifferentpatternentirelythatallowsforanynumberofargumentstobepassedtoafunction.Thisleavesthedooropenforsomeveryinterestingandunusualdesignpatterns.Functionscanbeappliedinpartorinwhole.

PartialapplicationinJavaScriptistheprocessofbindingvaluestooneormoreargumentsofafunctionthatreturnsanotherfunctionthatacceptstheremaining,unboundarguments.Similarly,curryingistheprocessoftransformingafunctionwithmanyargumentsintoafunctionwithoneargumentthatreturnsanotherfunctionthattakesmoreargumentsasneeded.

Thedifferencebetweenthetwomaynotbeclearnow,butitwillbeobviousintheend.

FunctionmanipulationActually,beforewegoanyfurtherandexplainjusthowtoimplementpartialapplicationandcurrying,weneedareview.Ifwe’regoingtotearJavaScript’sthickveneerofC-likesyntaxrightoffandexposeit’sfunctionalunderbelly,thenwe’regoingtoneedtounderstandhowprimitives,functions,andprototypesinJavaScriptwork;wewouldneverneedtoconsidertheseifwejustwantedtosetsomecookiesorvalidatesomeformfields.

Apply,call,andthethiskeywordInpurefunctionallanguages,functionsarenotinvoked;they’reapplied.JavaScriptworksthesamewayandevenprovidesutilitiesformanuallycallingandapplyingfunctions.Andit’sallaboutthethiskeyword,which,ofcourse,istheobjectthatthefunctionisamemberof.

Thecall()functionletsyoudefinethethiskeywordasthefirstargument.Itworksasfollows:

console.log(['Hello','world'].join(''))//normalway

console.log(Array.prototype.join.call(['Hello','world'],''));//using

call

Thecall()functioncanbeused,forexample,toinvokeanonymousfunctions:

console.log((function(){console.log(this.length)}).call([1,2,3]));

Theapply()functionisverysimilartothecall()function,butalittlemoreuseful:

console.log(Math.max(1,2,3));//returns3

console.log(Math.max([1,2,3]));//won'tworkforarraysthough

console.log(Math.max.apply(null,[1,2,3]));//butthiswillwork

Thefundamentaldifferenceisthat,whilethecall()functionacceptsalistofarguments,theapply()functionacceptsanarrayofarguments.

Thecall()andapply()functionsallowyoutowriteafunctiononceandtheninherititinotherobjectswithoutwritingthefunctionoveragain.AndtheyarebothmembersthemselvesoftheFunctionargument.

NoteThisisbonusmaterial,butwhenyouusethecall()functiononitself,somereallycoolthingscanhappen:

//thesetwolinesareequivalent

func.call(thisValue);

Function.prototype.call.call(func,thisValue);

BindingargumentsThebind()functionallowsyoutoapplyamethodtooneobjectwiththethiskeywordassignedtoanother.Internally,it’sthesameasthecall()function,butit’schainedtothemethodandreturnsanewboundedfunction.

It’sespeciallyusefulforcallbacks,asshowninthefollowingcodesnippet:

functionDrum(){

this.noise='boom';

this.duration=1000;

this.goBoom=function(){console.log(this.noise)};

}

vardrum=newDrum();

setInterval(drum.goBoom.bind(drum),drum.duration);

Thissolvesalotofproblemsinobject-orientedframeworks,suchasDojo,specificallytheproblemsofmaintainingthestatewhenusingclassesthatdefinetheirownhandlerfunctions.Butwecanusethebind()functionforfunctionalprogrammingtoo.

TipThebind()functionactuallydoespartialapplicationonitsown,thoughinaverylimitedway.

FunctionfactoriesRememberoursectiononclosuresinChapter2,FundamentalsofFunctionalProgramming?ClosuresaretheconstructsthatmakesitpossibletocreateausefulJavaScriptprogrammingpatternknownasfunctionfactories.Theyallowustomanuallybindargumentstofunctions.

First,we’llneedafunctionthatbindsanargumenttoanotherfunction:

functionbindFirstArg(func,a){

returnfunction(b){

returnfunc(a,b);

};

}

Thenwecanusethistocreatemoregenericfunctions:

varpowersOfTwo=bindFirstArg(Math.pow,2);

console.log(powersOfTwo(3));//8

console.log(powersOfTwo(5));//32

Anditcanworkontheotherargumenttoo:

functionbindSecondArg(func,b){

returnfunction(a){

returnfunc(a,b);

};

}

varsquareOf=bindSecondArg(Math.pow,2);

varcubeOf=bindSecondArg(Math.pow,3);

console.log(squareOf(3));//9

console.log(squareOf(4));//16

console.log(cubeOf(3));//27

console.log(cubeOf(4));//64

Theabilitytocreategenericfunctionsisveryimportantinfunctionalprogramming.Butthere’saclevertricktomakingthisprocessevenmoregeneralized.ThebindFirstArg()

functionitselftakestwoarguments,thefirstbeingafunction.IfwepassthebindFirstArgfunctionasafunctiontoitself,wecancreatebindablefunctions.Thiscanbebestdescribedwiththefollowingexample:

varmakePowersOf=bindFirstArg(bindFirstArg,Math.pow);

varpowersOfThree=makePowersOf(3);

console.log(powersOfThree(2));//9

console.log(powersOfThree(3));//27

Thisiswhythey’recalledfunctionfactories.

PartialapplicationNoticethatourfunctionfactoryexample’sbindFirstArg()andbindSecondArg()functionsonlyworkforfunctionsthathaveexactlytwoarguments.Wecouldwritenewonesthatworkfordifferentnumbersofarguments,butthatwouldworkawayfromourmodelofgeneralization.

Whatweneedispartialapplication.

NotePartialapplicationistheprocessofbindingvaluestooneormoreargumentsofafunctionthatreturnsapartially-appliedfunctionthatacceptstheremaining,unboundarguments.

Unlikethebind()functionandotherbuilt-inmethodsoftheFunctionobject,we’llhavetocreateourownfunctionsforpartialapplicationandcurrying.Therearetwodistinctwaystodothis.

Asastand-alonefunction,thatis,varpartial=function(func){...Asapolyfill,thatis,Function.prototype.partial=function(){...

Polyfillsareusedtoaugmentprototypeswithnewfunctionsandwillallowustocallournewfunctionsasmethodsofthefunctionthatwewanttopartiallyapply.Justlikethis:myfunction.partial(arg1,arg2,…);

PartialapplicationfromtheleftHere’swhereJavaScript’sapply()andcall()utilitiesbecomeusefulforus.Let’slookatapossiblepolyfillfortheFunctionobject:

Function.prototype.partialApply=function(){

varfunc=this;

args=Array.prototype.slice.call(arguments);

returnfunction(){

returnfunc.apply(this,args.concat(

Array.prototype.slice.call(arguments)

));

};

};

Asyoucansee,itworksbyslicingtheargumentsspecialvariable.

NoteEveryfunctionhasaspeciallocalvariablecalledargumentsthatisanarray-likeobjectoftheargumentspassedtoit.It’stechnicallynotanarray.ThereforeitdoesnothaveanyoftheArraymethodssuchassliceandforEach.That’swhyweneedtouseArray’sslice.callmethodtoslicethearguments.

Andnowlet’sseewhathappenswhenweuseitinanexample.Thistime,let’sgetawayfromthemathandgoforsomethingalittlemoreuseful.We’llcreatealittleapplicationthatconvertsnumberstohexadecimalvalues.

functionnums2hex(){

functioncomponentToHex(component){

varhex=component.toString(16);

//makesurethereturnvalueis2digits,i.e.0cor12

if(hex.length==1){

return"0"+hex;

}

else{

returnhex;

}

}

returnArray.prototype.map.call(arguments,componentToHex).join('');

}

//thefunctionworksonanynumberofinputs

console.log(nums2hex());//''

console.log(nums2hex(100,200));//'64c8'

console.log(nums2hex(100,200,255,0,123));//'64c8ff007b'

//butwecanusethepartialfunctiontopartiallyapply

//arguments,suchastheOUIofamacaddress

varmyOUI=123;

vargetMacAddress=nums2hex.partialApply(myOUI);

console.log(getMacAddress());//'7b'

console.log(getMacAddress(100,200,2,123,66,0,1));//

'7b64c8027b420001'

//orwecanconvertrgbvaluesofredonlytohexadecimal

varshadesOfRed=nums2hex.partialApply(255);

console.log(shadesOfRed(123,0));//'ff7b00'

console.log(shadesOfRed(100,200));//'ff64c8'

Thisexampleshowsthatwecanpartiallyapplyargumentstoagenericfunctionandgetanewfunctioninreturn.Thisfirstexampleisleft-to-right,whichmeansthatwecanonlypartiallyapplythefirst,left-mostarguments.

PartialapplicationfromtherightInordertoapplyargumentsfromtheright,wecandefineanotherpolyfill.

Function.prototype.partialApplyRight=function(){

varfunc=this;

args=Array.prototype.slice.call(arguments);

returnfunction(){

returnfunc.apply(

this,

[].slice.call(arguments,0)

.concat(args));

};

};

varshadesOfBlue=nums2hex.partialApplyRight(255);

console.log(shadesOfBlue(123,0));//'7b00ff'

console.log(shadesOfBlue(100,200));//'64c8ff'

varsomeShadesOfGreen=nums2hex.partialApplyRight(255,0);

console.log(shadesOfGreen(123));//'7bff00'

console.log(shadesOfGreen(100));//'64ff00'

Partialapplicationhasallowedustotakeaverygenericfunctionandextractmorespecificfunctionsoutofit.Butthebiggestflawinthismethodisthatthewayinwhichtheargumentsarepassed,asinhowmanyandinwhatorder,canbeambiguous.Andambiguityisneveragoodthinginprogramming.There’sabetterwaytodothis:currying.

CurryingCurryingistheprocessoftransformingafunctionwithmanyargumentsintoafunctionwithoneargumentthatreturnsanotherfunctionthattakesmoreargumentsasneeded.Formally,afunctionwithNargumentscanbetransformedintoafunctionchainofNfunctions,eachwithonlyoneargument.

Acommonquestionis:whatisthedifferencebetweenpartialapplicationandcurrying?Whileit’struethatpartialapplicationreturnsavaluerightawayandcurryingonlyreturnsanothercurriedfunctionthattakesthenextargument,thefundamentaldifferenceisthatcurryingallowsformuchbettercontrolofhowargumentsarepassedtothefunction.We’llseejusthowthat’strue,butfirstweneedtocreatefunctiontoperformthecurrying.

Here’sourpolyfillforaddingcurryingtotheFunctionprototype:

Function.prototype.curry=function(numArgs){

varfunc=this;

numArgs=numArgs||func.length;

//recursivelyacquirethearguments

functionsubCurry(prev){

returnfunction(arg){

varargs=prev.concat(arg);

if(args.length<numArgs){

//recursivecase:westillneedmoreargs

returnsubCurry(args);

}

else{

//basecase:applythefunction

returnfunc.apply(this,args);

}

};

}

returnsubCurry([]);

};

ThenumArgsargumentletsusoptionallyspecifythenumberofargumentsthefunctionbeingcurriedneedsifit’snotexplicitlydefined.

Let’slookathowtouseitwithinourhexadecimalapplication.We’llwriteafunctionthatconvertsRGBvaluestoahexadecimalstringthatisappropriateforHTML:

functionrgb2hex(r,g,b){

//nums2hexispreviouslydefinedinthischapter

return'#'+nums2hex(r)+nums2hex(g)+nums2hex(b);

}

varhexColors=rgb2hex.curry();

console.log(hexColors(11))//returnsacurriedfunction

console.log(hexColors(11,12,123))//returnsacurriedfunction

console.log(hexColors(11)(12)(123))//returns#0b0c7b

console.log(hexColors(210)(12)(0))//returns#d20c00

Itwillreturnthecurriedfunctionuntilallneededargumentsarepassedin.Andthey’repassedinthesameleft-to-rightorderasdefinedbythefunctionbeingcurried.

Butwecanstepitupanotchanddefinethemorespecificfunctionsthatweneedasfollows:

varreds=function(g,b){returnhexColors(255)(g)(b)};

vargreens=function(r,b){returnhexColors(r)(255)(b)};

varblues=function(r,g){returnhexColors(r)(g)(255)};

console.log(reds(11,12))//returns#ff0b0c

console.log(greens(11,12))//returns#0bff0c

console.log(blues(11,12))//returns#0b0cff

Sothat’sanicewaytousecurrying.Butifwejustwanttocurryournums2hex()functiondirectly,werunintoalittlebitoftrouble.Andthat’sbecausethefunctiondoesn’tdefineanyarguments,itjustletsyoupassasmanyargumentsinasyouwant.Sowehavetodefinethenumberofarguments.Wedothatwiththeoptionalparametertothecurryfunctionthatallowsustosetthenumberofargumentsofthefunctionbeingcurried.

varhexs=nums2hex.curry(2);

console.log(hexs(11)(12));//returns0b0c

console.log(hexs(11));//returnsfunction

console.log(hexs(110)(12)(0));//incorrect

Thereforecurryingdoesnotworkwellwithfunctionsthatacceptvariablenumbersofarguments.Forsomethinglikethat,partialapplicationispreferred.

Allofthisisn’tjustforthebenefitoffunctionfactoriesandcodereuse.Curryingandpartialapplicationplayintoabiggerpatternknownascomposition.

FunctioncompositionFinally,wehavearrivedatfunctioncomposition.

Infunctionalprogramming,wewanteverythingtobeafunction.Weespeciallywantunaryfunctionsifpossible.Ifwecanconvertallfunctionstounaryfunctions,thenmagicalthingscanhappen.

NoteUnaryfunctionsarefunctionsthattakeonlyasingleinput.Functionswithmultipleinputsarepolyadic,butweusuallysaybinaryforfunctionsthataccepttwoinputsandternaryforthreeinputs.Somefunctionsdon’tacceptaspecificnumberofinputs;wecallthosevariadic.

Manipulatingfunctionsandtheiracceptablenumberofinputscanbeextremelyexpressive.Inthissection,wewillexplorehowtocomposenewfunctionsfromsmallerfunctions:littleunitsoflogicthatcombineintowholeprogramsthataregreaterthanthesumofthefunctionsontheirown.

ComposeComposingfunctionsallowsustobuildcomplexfunctionsfrommanysimple,genericfunctions.Bytreatingfunctionsasbuildingblocksforotherfunctions,wecanbuildtrulymodularapplicationswithexcellentreadabilityandmaintainability.

Beforewedefinethecompose()polyfill,youcanseehowitallworkswiththesefollowingexamples:

varroundedSqrt=Math.round.compose(Math.sqrt)

console.log(roundedSqrt(5));//Returns:2

varsquaredDate=roundedSqrt.compose(Date.parse)

console.log(squaredDate("January1,2014"));//Returns:1178370

Inmath,thecompositionofthefandgvariablesisdefinedasf(g(x)).InJavaScript,thiscanbewrittenas:

varcompose=function(f,g){

returnfunction(x){

returnf(g(x));

};

};

Butifweleftitatthat,wewouldlosetrackofthethiskeyword,amongotherproblems.Thesolutionistousetheapply()andcall()utilities.Comparedtocurry,thecompose()polyfillisquitesimple.

Function.prototype.compose=function(prevFunc){

varnextFunc=this;

returnfunction(){

returnnextFunc.call(this,prevFunc.apply(this,arguments));

}

}

Toshowhowit’sused,let’sbuildacompletelycontrivedexample,asfollows:

functionfunction1(a){returna+'1';}

functionfunction2(b){returnb+'2';}

functionfunction3(c){returnc+'3';}

varcomposition=function3.compose(function2).compose(function1);

console.log(composition('count'));//returns'count123'

Didyounoticethatthefunction3parameterwasappliedfirst?Thisisveryimportant.Functionsareappliedfromrighttoleft.

Sequence–composeinreverseBecausemanypeopleliketoreadthingsfromthelefttotheright,itmightmakesensetoapplythefunctionsinthatordertoo.We’llcallthisasequenceinsteadofacomposition.

Toreversetheorder,allweneedtodoisswapthenextFuncandprevFuncparameters.

Function.prototype.sequence=function(prevFunc){

varnextFunc=this;

returnfunction(){

returnprevFunc.call(this,nextFunc.apply(this,arguments));

}

}

Thisallowsustonowcallthefunctionsinamorenaturalorder.

varsequences=function1.sequence(function2).sequence(function3);

console.log(sequences('count'));//returns'count123'

CompositionsversuschainsHerearefivedifferentimplementationsofthesamefloorSqrt()functionalcomposition.Theyseemtobeidentical,buttheydeservescrutiny.

functionfloorSqrt1(num){

varsqrtNum=Math.sqrt(num);

varfloorSqrt=Math.floor(sqrtNum);

varstringNum=String(floorSqrt);

returnstringNum;

}

functionfloorSqrt2(num){

returnString(Math.floor(Math.sqrt(num)));

}

functionfloorSqrt3(num){

return[num].map(Math.sqrt).map(Math.floor).toString();

}

varfloorSqrt4=String.compose(Math.floor).compose(Math.sqrt);

varfloorSqrt5=Math.sqrt.sequence(Math.floor).sequence(String);

//allfunctionscanbecalledlikethis:

floorSqrt<N>(17);//Returns:4

Butthereareafewkeydifferencesweshouldgoover:

Obviouslythefirstmethodisverboseandinefficient.Thesecondmethodisaniceone-liner,butthisapproachbecomesveryunreadableafteronlyafewfunctionsareapplied.

NoteTosaythatlesscodeisbetterismissingthepoint.Codeismoremaintainablewhentheeffectiveinstructionsaremoreconcise.Ifyoureducethenumberofcharactersonthescreenwithoutchangingtheeffectiveinstructionscarriedout,thishasthecompleteoppositeeffect—codebecomeshardertounderstand,anddecidedlylessmaintainable;forexample,whenweusenestedternaryoperators,orwechainseveralcommandstogetheronasingleline.Theseapproachesreducetheamountof‘codeonthescreen’,buttheydon’treducethenumberofstepsactuallybeingspecifiedbythatcode.Sotheeffectistoobfuscateandmakethecodehardertounderstand.Thekindofconcisenessthatmakescodeeasiertomaintainisthatwhicheffectivelyreducesthespecifiedinstructions(forexample,byusingasimpleralgorithmthataccomplishesthesameresultwithfewerand/orsimplersteps),orwhenwesimplyreplacecodewithamessage,forinstance,invokingathird-partylibrarywithawell-documentedAPI.

Thethirdapproachisachainofarrayfunctions,notablythemapfunction.Thisworksfairlywell,butitisnotmathematicallycorrect.Here’sourcompose()functioninaction.Allmethodsareforcedtobeunary,purefunctionsthatencouragetheuseofbetter,simpler,andsmallerfunctionsthatdoone

thinganddoitwell.Thelastapproachusesthecompose()functioninreversesequence,whichisjustasvalid.

ProgrammingwithcomposeThemostimportantaspectofcomposeisthat,asidefromthefirstfunctionthatisapplied,itworksbestwithpure,unaryfunctions:functionsthattakeonlyoneargument.

Theoutputofthefirstfunctionthatisappliedissenttothenextfunction.Thismeansthatthefunctionmustacceptwhatthepreviousfunctionpassedtoit.Thisisthemaininfluencebehindtypesignatures.

NoteTypeSignaturesareusedtoexplicitlydeclarewhattypesofinputthefunctionacceptsandwhattypeitoutputs.TheywerefirstusedbyHaskell,whichactuallyusedtheminthefunctiondefinitionstobeusedbythecompiler.But,inJavaScript,wejustputtheminacodecomment.Theylooksomethinglikethis:foo::arg1->argN->output

Examples:

//getStringLength::String->IntfunctiongetStringLength(s){return

s.length};

//concatDates::Date->Date->[Date]functionconcatDates(d1,d2){return

[d1,d2]};

//pureFunc::(int->Bool)->[int]->[int]pureFunc(func,arr){return

arr.filter(func)}

Inordertotrulyreapthebenefitsofcompose,anyapplicationwillneedaheftycollectionofunary,purefunctions.Thesearethebuildingblocksthatarecomposedintolargerfunctionsthat,inturn,areusedtomakeapplicationsthatareverymodular,reliable,andmaintainable.

Let’sgothroughanexample.Firstwe’llneedmanybuilding-blockfunctions.Someofthembuildupontheothersasfollows:

//stringToArray::String->[Char]

functionstringToArray(s){returns.split('');}

//arrayToString::[Char]->String

functionarrayToString(a){returna.join('');}

//nextChar::Char->Char

functionnextChar(c){

returnString.fromCharCode(c.charCodeAt(0)+1);}

//previousChar::Char->Char

functionpreviousChar(c){

returnString.fromCharCode(c.charCodeAt(0)-1);}

//higherColorHex::Char->Char

functionhigherColorHex(c){returnc>='f'?'f':

c=='9'?'a':

nextChar(c)}

//lowerColorHex::Char->Char

functionlowerColorHex(c){returnc<='0'?'0':

c=='a'?'9':

previousChar(c);}

//raiseColorHexes::String->String

functionraiseColorHexes(arr){returnarr.map(higherColorHex);}

//lowerColorHexes::String->String

functionlowerColorHexes(arr){returnarr.map(lowerColorHex);}

Nowlet’scomposesomeofthemtogether.

varlighterColor=arrayToString

.compose(raiseColorHexes)

.compose(stringToArray)

vardarkerColor=arrayToString

.compose(lowerColorHexes)

.compose(stringToArray)

console.log(lighterColor('af0189'));//Returns:'bf129a'

console.log(darkerColor('af0189'));//Returns:'9e0078'

Wecanevenusecompose()andcurry()functionstogether.Infact,theyworkverywelltogether.Let’sforgetogetherthecurryexamplewithourcomposeexample.Firstwe’llneedourhelperfunctionsfrombefore.

//component2hex::Ints->Int

functioncomponentToHex(c){

varhex=c.toString(16);

returnhex.length==1?"0"+hex:hex;

}

//nums2hex::Ints*->Int

functionnums2hex(){

returnArray.prototype.map.call(arguments,componentToHex).join('');

}

Firstweneedtomakethecurriedandpartial-appliedfunctions,thenwecancomposethemtoourothercomposedfunctions.

varlighterColors=lighterColor

.compose(nums2hex.curry());

vardarkerRed=darkerColor

.compose(nums2hex.partialApply(255));

VarlighterRgb2hex=lighterColor

.compose(nums2hex.partialApply());

console.log(lighterColors(123,0,22));//Returns:8cff11

console.log(darkerRed(123,0));//Returns:ee6a00

console.log(lighterRgb2hex(123,200,100));//Returns:8cd975

Therewehaveit!Thefunctionsreadreallywellandmakealotofsense.Wewereforcedtobeginwithlittlefunctionsthatjustdidonething.Thenwewereabletoputtogetherfunctionswithmoreutility.

Let’slookatonelastexample.Here’safunctionthatlightensanRBGvaluebyavariableamount.Thenwecanusecompositiontocreatenewfunctionsfromit.

//lighterColorNumSteps::string->num->string

functionlighterColorNumSteps(color,n){

for(vari=0;i<n;i++){

color=lighterColor(color);

}

returncolor;

}

//nowwecancreatefunctionslikethis:

varlighterRedNumSteps=lighterColorNumSteps.curry().compose(reds)(0,0);

//andusethemlikethis:

console.log(lighterRedNumSteps(5));//Return:'ff5555'

console.log(lighterRedNumSteps(2));//Return:'ff2222'

Inthesameway,wecouldeasilycreatemorefunctionsforcreatinglighteranddarkerblues,greens,grays,purples,anythingyouwant.ThisisareallygreatwaytoconstructanAPI.

Wejustbarelyscratchedthesurfaceofwhatfunctioncompositioncando.WhatcomposedoesistakecontrolawayfromJavaScript.NormallyJavaScriptwillevaluatelefttoright,butnowtheinterpreterissaying“OK,somethingelseisgoingtotakecareofthis,I’lljustmoveontothenext.”Andnowthecompose()functionhascontrolovertheevaluationsequence!

ThisishowLazy.js,Bacon.jsandothershavebeenabletoimplementthingssuchaslazyevaluationandinfinitesequences.Upnext,we’lllookintohowthoselibrariesareused.

MostlyfunctionalprogrammingWhatisaprogramwithoutsideeffects?Aprogramthatdoesnothing.

Complementingourcodewithfunctionalcodewithunavoidableside-effectscanbecalled“mostlyfunctionalprogramming.”Usingmultipleparadigmsinthesamecodebaseandapplyingthemwheretheyaremostoptimalisthebestapproach.Mostlyfunctionalprogrammingishoweventhepure,traditionalfunctionalprogramsaremodelled:keepmostofthelogicinpurefunctionsandinterfacewithimperativecode.

Andthisishowwe’regoingtowritealittleapplicationofourown.

Inthisexample,wehaveabossthattellsusthatweneedawebapplicationforourcompanythattracksthestatusoftheemployees’availability.Alltheemployeesatthisfictionalcompanyonlyhaveonejob:usingourwebsite.Staffwillsigninwhentheygettoworkandsignoutwhentheyleave.Butthat’snotenough,italsoneedstoautomaticallyupdatethecontentasitchanges,soourbossdoesn’thavetokeeprefreshingthepages.

We’regoingtouseLazy.jsasourfunctionallibrary.Andwe’realsogoingtobelazy:insteadofworryingabouthandlingalltheuserslogginginandout,WebSockets,databases,andmore,we’lljustpretendthere’sagenericapplicationobjectthatdoesthisforusandjusthappenstohavetheperfectAPI.

Sofornow,let’sjustgettheuglypartsoutoftheway,thepartsthatinterfaceandcreateside-effects.

functionReceptor(name,available){

this.name=name;

this.available=available;//mutablestate

this.render=function(){

output='<li>';

output+=this.available?

this.name+'isavailable':

this.name+'isnotavailable';

output+='</li>';

returnoutput;

}

}

varme=newReceptor;

varreceptors=app.getReceptors().push(me);

app.container.innerHTML=receptors.map(function(r){

returnr.render();

}).join('');

Thiswouldbesufficientforjustdisplayingalistofavailabilities,butwewantittobereactive,whichbringsustoourfirstobstacle.

ByusingtheLazy.jslibrarytostoretheobjectsinasequence,whichwon’tactuallycomputeanythinguntilthetoArray()methodiscalled,wecantakeadvantageofitslazinesstoprovideasortoffunctionalreactiveprogramming.

varlazyReceptors=Lazy(receptors).map(function(r){

returnr.render();

});

app.container.innerHTML=lazyReceptors.toArray().join('');

BecausetheReceptor.render()methodreturnsnewHTMLinsteadofmodifyingthecurrentHTML,allwehavetodoissettheinnerHTMLparametertoitsoutput.

We’llalsohavetotrustthatourgenericapplicationforusermanagementwillprovidecallbackmethodsforustouse.

app.onUserLogin=function(){

this.available=true;

app.container.innerHTML=lazyReceptors.toArray().join('');

};

app.onUserLogout=function(){

this.available=false;

app.container.innerHTML=lazyReceptors.toArray().join('');

};

Thisway,anytimeauserlogsinorout,thelazyReceptorsparameterwillbecomputedagainandtheavailabilitylistwillbeprintedwiththemostrecentvalues.

HandlingeventsButwhatiftheapplicationdoesn’tprovidecallbacksforwhentheuserlogsinandout?Callbacksaremessyandcanquicklyturnaprogramintospaghetticode.Instead,wecandetermineitourselvesbyobservingtheuserdirectly.Iftheuserhasthewebpageinfocus,thenhe/shemustbeactiveandavailable.WecanuseJavaScript’sfocusandblureventsforthis.

window.addEventListener('focus',function(event){

me.available=true;

app.setReceptor(me.name,me.available);//justgowithit

container.innerHTML=lazyReceptors.toArray().join('');

});

window.addEventListener('blur',function(event){

me.available=false;

app.setReceptor(me.name,me.available);

container.innerHTML=lazyReceptors.toArray().join('');

});

Waitasecond,aren’teventsreactivetoo?Cantheybelazilycomputedaswell?TheycanintheLazy.jslibrary,wherethere’sevenahandymethodforthis.

varfocusedReceptors=Lazy.events(window,"focus").each(function(e){

me.available=true;

app.setReceptor(me.name,me.available);

container.innerHTML=lazyReceptors.toArray().join('');

});

varblurredReceptors=Lazy.events(window,"blur").each(function(e){

me.available=false;

app.setReceptor(me.name,me.available);

container.innerHTML=lazyReceptors.toArray().join('');

});

Easyaspie.

NoteByusingtheLazy.jslibrarytohandleevents,wecancreateaninfinitesequenceofevents.Eachtimetheeventisfired,theLazy.each()functionisabletoiterateonemoretime.

Ourbosslikestheapplicationsofar,butshepointsoutthatifanemployeeneverlogsoutbeforeleavingforthedaywithoutclosingthepage,thentheapplicationsaystheemployeeisstillavailable.

Tofigureoutifanemployeeisactiveonthewebsite,wecanmonitorthekeyboardandmouseevents.Let’ssaythey’reconsideredtobeunavailableafter30minutesofnoactivity.

vartimeout=null;

varinputs=Lazy.events(window,"mousemove").each(function(e){

me.available=true;

container.innerHTML=lazyReceptors.toArray().join('');

clearTimeout(timeout);

timeout=setTimeout(function(){

me.available=false;

container.innerHTML=lazyReceptors.toArray().join('');

},1800000);//30minutes

});

TheLazy.jslibraryhasmadeitveryeasyforustohandleeventsasaninfinitestreamthatwecanmapover.Itmakesthispossiblebecauseitusesfunctioncompositiontotakecontroloftheorderofexecution.

Butthere’salittleproblemwithallofthis.Whatiftherearenouserinputeventsthatwecanlatchonto?Whatif,instead,thereisapropertyvaluethatchangesallthetime?Inthenextsection,we’llinvestigateexactlythisissue.

FunctionalreactiveprogrammingLet’sbuildanotherkindofapplicationthatworksinmuchthesameway;onethatusesfunctionalprogrammingtoreacttochangesinstate.But,thistime,theapplicationwon’tbeabletorelyoneventlisteners.

ImagineforamomentthatyouworkforanewsmediacompanyandyourbosstellsyoutocreateawebapplicationthattracksgovernmentelectionresultsonElectionDay.Dataiscontinuouslyflowinginaslocalprecinctsturnintheirresults,sotheresultstodisplayonthepageareveryreactive.Butwealsoneedtotracktheresultsbyeachregion,sotherewillbemultipleobjectstotrack.

Ratherthancreatingabigobject-orientedhierarchytomodeltheinterface,wecandescribeitdeclarativelyasimmutabledata.Wecantransformitwithchainsofpureandsemi-purefunctionswhoseonlyultimatesideeffectsareupdatingwhateverbitsofstateabsolutelymustbeheldonto(ideally,notmany).

Andwe’llusetheBacon.jslibrary,whichwillallowustoquicklydevelopFunctionalReactiveProgramming(FRP)applications.Theapplicationwillonlybeusedonedayoutoftheyear(ElectionDay),andourbossthinksitshouldtakeaproportionalamountoftime.WithfunctionalprogrammingandalibrarysuchasBacon.js,we’llgetitdoneinhalfthetime.

Butfirst,we’regoingtoneedsomeobjectstorepresentthevotingregions,suchasstates,provinces,districts,andsoon.

functionRegion(name,percent,parties){

//mutableproperties:

this.name=name;

this.percent=percent;//%ofprecinctsreported

this.parties=parties;//politicalparties

//returnanHTMLrepresentation

this.render=function(){

varlis=this.parties.map(function(p){

return'<li>'+p.name+':'+p.votes+'</li>';

});

varoutput='<h2>'+this.name+'</h2>';

output+='<ul>'+lis.join('')+'</ul>';

output+='Percentreported:'+this.percent;

returnoutput;

}

}

functiongetRegions(data){

returnJSON.parse(data).map(function(obj){

returnnewRegion(obj.name,obj.percent,obj.parties);

});

}

varurl='http://api.server.com/election-data?format=json';

vardata=jQuery.ajax(url);

varregions=getRegions(data);

app.container.innerHTML=regions.map(function(r){

returnr.render();

}).join('');

Whiletheabovewouldbesufficientforjustdisplayingastaticlistofelectionresults,weneedawaytoupdatetheregionsdynamically.It’stimetocookupsomeBaconandFRP.

ReactivityBaconhasafunction,Bacon.fromPoll(),thatletsuscreateaneventstream,wheretheeventisjustafunctionthatiscalledonthegiveninterval.Andthestream.subscribe()functionletsussubscribeahandlerfunctiontothestream.Becauseit’slazy,thestreamwillnotactuallydoanythingwithoutasubscriber.

vareventStream=Bacon.fromPoll(10000,function(){

returnBacon.Next;

});

varsubscriber=eventStream.subscribe(function(){

varurl='http://api.server.com/election-data?format=json';

vardata=jQuery.ajax(url);

varnewRegions=getRegions(data);

container.innerHTML=newRegions.map(function(r){

returnr.render();

}).join('');

});

Byessentiallyputtingitinaloopthatrunsevery10seconds,wecouldgetthejobdone.Butthismethodwouldhammer-pingthenetworkandisincrediblyinefficient.Thatwouldnotbeveryfunctional.Instead,let’sdigalittledeeperintotheBacon.jslibrary.

InBacon,thereareEventStreamsandPropertiesparameters.Propertiescanbethoughtofas“magic”variablesthatchangeovertimeinresponsetoevents.They’renotreallymagicbecausetheystillrelyonastreamofevents.ThePropertychangesovertimeinrelationtoitsEventStream.

TheBacon.jslibraryhasanothertrickupitssleeve.TheBacon.fromPromise()functionisawaytoemiteventsintoastreambyusingpromises.AndasofjQueryversion1.5.0,jQueryAJAXimplementsthepromisesinterface.SoallweneedtodoiswriteanAJAXsearchfunctionthatemitseventswhentheasynchronouscalliscomplete.Everytimethepromiseisresolved,itcallstheEvenStream’ssubscribers.

varurl='http://api.server.com/election-data?format=json';

vareventStream=Bacon.fromPromise(jQuery.ajax(url));

varsubscriber=eventStream.onValue(function(data){

newRegions=getRegions(data);

container.innerHTML=newRegions.map(function(r){

returnr.render();

}).join('');

}

Apromisecanbethoughtofasaneventualvalue;withtheBacon.jslibrary,wecanlazilywaitontheeventualvalues.

PuttingitalltogetherNowthatwehavethereactivitycovered,wecanfinallyplaywithsomecode.

Wecanmodifythesubscriberwithchainsofpurefunctionstodothingssuchasaddingupatotalandfilteringoutunwantedresults,andwedoitallwithinonclick()handlerfunctionsforbuttonsthatwecreate.

//createtheeventStreamoutsideofthefunctions

vareventStream=Bacon.onPromise(jQuery.ajax(url));

varsubscribe=null;

varurl='http://api.server.com/election-data?format=json';

//ourun-modifiedsubscriber

$('button#showAll').click(function(){

varsubscriber=eventStream.onValue(function(data){

varnewRegions=getRegions(data).map(function(r){

returnnewRegion(r.name,r.percent,r.parties);

});

container.innerHTML=newRegions.map(function(r){

returnr.render();

}).join('');

});

});

//abuttonforshowingthetotalvotes

$('button#showTotal').click(function(){

varsubscriber=eventStream.onValue(function(data){

varemptyRegion=newRegion('empty',0,[{

name:'Republican',votes:0

},{

name:'Democrat',votes:0

}]);

vartotalRegions=getRegions(data).reduce(function(r1,r2){

newParties=r1.parties.map(function(x,i){

return{

name:r1.parties[i].name,

votes:r1.parties[i].votes+r2.parties[i].votes

};

});

newRegion=newRegion('Total',(r1.percent+r2.percent)/2,

newParties);

returnnewRegion;

},emptyRegion);

container.innerHTML=totalRegions.render();

});

});

//abuttonforonlydisplayingregionsthatarereporting>50%

$('button#showMostlyReported').click(function(){

varsubscriber=eventStream.onValue(function(data){

varnewRegions=getRegions(data).map(function(r){

if(r.percent>50)returnr;

elsereturnnull;

}).filter(function(r){returnr!=null;});

container.innerHTML=newRegions.map(function(r){

returnr.render();

}).join('');

});

});

Thebeautyofthisisthat,whenusersclickbetweenthebuttons,theeventstreamdoesn’tchangebutthesubscriberdoes,whichmakesitallworksmoothly.

SummaryJavaScriptisabeautifullanguage.

Itsinnerbeautyreallyshineswithfunctionalprogramming.It’swhatempowersitsexcellentextendibility.Justthefactthatitallowsfirst-classfunctionsthatcandosomanythingsiswhatopensthefunctionalfloodgates.Conceptsbuildontopofeachother,stackinguphigherandhigher.

Inthischapter,wedovehead-firstintothefunctionalparadigminJavaScript.Wecoveredfunctionfactories,currying,functioncompositionandeverythingrequiredtomakeitwork.Webuiltanextremelymodularapplicationthatusedtheseconcepts.Andthenweshowedhowtousesomefunctionallibrariesthatusethesesameconceptsthemselves,namelyfunctioncomposition,tomanipulatetheorderofexecution.

Throughoutthechapter,wecoveredseveralstylesoffunctionalprogramming:datagenericprogramming,mostly-functionalprogramming,andfunctionalreactiveprogramming.They’reallnotthatdifferentfromeachother,they’rejustdifferentpatternsforapplyingfunctionalprogramingindifferentsituations.

Inthepreviouschapter,somethingcalledCategoryTheorywasbrieflymentioned.Inthenextchapter,we’regoingtolearnalotmoreaboutwhatitisandhowtouseit.

Chapter5.CategoryTheoryThomasWatsonwasfamouslyquotedassaying,“Ithinkthereisaworldmarketformaybefivecomputers”.Thatwasin1948.Backthen,everybodyknewthatcomputerswouldonlybeusedfortwothings:mathandengineering.Noteventhebiggestmindsintechcouldpredictthat,oneday,computerswouldbeabletotranslateSpanishtoEnglish,orsimulateentireweathersystems.Atthetime,thefastestmachinewasIBM’sSSEC,clockinginat50multiplicationspersecond,thedisplayterminalwasn’tdueuntil15yearslaterandmultiple-processingmeantmultipleuserterminalssharingasingleprocessor.Thetransistorchangedeverything,buttech’svisionariesstillmissedthemark.KenOlsonmadeanotherfamouslyfoolishpredictionwhen,in1977,hesaid“Thereisnoreasonanyonewouldwantacomputerintheirhome”.

Itseamsobvioustousnowthatcomputersarenotjustforscientistsandengineers,butthat’shindsight.Theideathatmachinescandomorethanjustmathwasanythingbutintuitive70yearsago.Watsondidn’tjustfailtorealizehowcomputerscouldtransformasociety,hefailedtorealizethetransformativeandevolvingpowersofmathematics.

Butthepotentialofcomputersandmathwasnotlostoneverybody.JohnMcCarthyinventedLispin1958,arevolutionaryalgorithm-basedlanguagethatusheredinaneweraincomputing.Sinceitsinception,Lispwasinstrumentalintheideaofusingabstractionlayers—compilers,interpreters,virtualization—topushforwardtheprogressionofcomputersfromhardcoremathmachinestowhattheyaretoday.

FromLispcameScheme,adirectancestorofJavaScript.Nowthatbringsusfullcircle.Ifcomputersare,attheircore,machinesthatjustdomath,thenitstandstoreasonthatamath-basedprogrammingparadigmwouldexcel.

Theterm“math”isbeingusedherenottodescribethe“numbercrunching”thatcomputerscanobviouslydo,buttodescribediscretemathematics:thestudyofdiscrete,mathematicalstructuressuchasstatementsinlogicortheinstructionsofacomputerlanguage.Bytreatingcodeasadiscretemathematicalstructure,wecanapplyconceptsandideasinmathtoit.Thisiswhathasmadefunctionalprogrammingsoinstrumentalinartificialintelligence,graphsearch,patternrecognitionandotherbigchallengesincomputerscience.

Inthischapter,wewillexperimentwithsomeoftheseconceptsandtheirapplicationsineverydayprogrammingchallenges.Theywillinclude:

CategorytheoryMorphismsFunctorsMaybesPromisesLensesFunctioncomposition

Withtheseconcepts,we’llbeabletowriteentirelibrariesandAPIsveryeasilyandsafely.

Andwe’llgofromexplainingcategorytheorytoformallyimplementingitinJavaScript.

CategorytheoryCategorytheoryisthetheoreticalconceptthatempowersfunctioncomposition.Categorytheoryandfunctioncompositiongotogetherlikeenginedisplacementandhorsepower,likeNASAandthespaceshuttle,likegoodbeerandamugtopouritin.Basically,youcan’thaveonewithouttheother.

CategorytheoryinanutshellCategorytheoryreallyisn’ttoodifficultaconcept.Itsplaceinmathislargeenoughtofillupanentiregraduate-levelcollegecourse,butitsplaceincomputerprogrammingcanbesummedupquiteeasily.

Einsteinoncesaid,“Ifyoucan’texplainittoa6-year-old,youdon’tknowityourself”.Thus,inthespiritofexplainingittoa6-year-old,categorytheoryisjustconnectingthedots.Althoughitmaybegrosslyover-simplifyingcategorytheory,itdoesdoagoodjobofexplainingwhatweneedtoknowinastraightforwardmanner.

Firstyou’llneedtoknowsometerminology.Categoriesarejustsetswiththesametype.InJavaScript,they’rearraysorobjectsthatcontainvariablesthatareexplicitlydeclaredasnumbers,strings,Booleans,dates,nodes,andsoon.Morphismsarepurefunctionsthat,whengivenaspecificsetofinputs,alwaysreturnthesameoutput.Homomorphicoperationsarerestrictedtoasinglecategory,whilepolymorphicoperationscanoperateonmultiplecategories.Forexample,thehomomorphicfunctionmultiplicationonlyworksonnumbers,butthepolymorphicfunctionadditioncanworkonstringstoo.

Thefollowingdiagramshowsthreecategories—A,B,andC—andtwomorphisms—ƒandɡ.

Categorytheorytellsusthat,whenwehavetwomorphismswherethecategoryofthefirstoneistheexpectedinputoftheother,thentheycanbecomposedtothefollowing:

Theƒogsymbolisthecompositionofmorphismsƒandg.Nowwecanjustconnectthedots.

Andthat’sallitreallyis,justconnectingdots.

TypesafetyLet’sconnectsomedots.Categoriescontaintwothings:

1. Objects(inJavaScript,types).2. Morphisms(inJavaScript,purefunctionsthatonlyworkontypes).

Thesearethetermsgiventocategorytheorybymathematicians,sothereissomeunfortunatenomenclatureoverloadingwithourJavaScriptterminology.ObjectsincategorytheoryaremorelikevariableswithanexplicitdatatypeandnotcollectionsofpropertiesandvalueslikeintheJavaScriptdefinitionofobjects.Morphismsarejustpurefunctionsthatusethosetypes.

SoapplyingtheideaofcategorytheorytoJavaScriptisprettyeasy.UsingcategorytheoryinJavaScriptmeansworkingwithonecertaindatatypepercategory.Datatypesarenumbers,strings,arrays,dates,objects,Booleans,andsoon.But,withnostricttypesysteminJavaScript,thingscangoawry.Sowe’llhavetoimplementourownmethodofensuringthatthedataiscorrect.

TherearefourprimitivedatatypesinJavaScript:numbers,strings,Booleans,andfunctions.Wecancreatetypesafetyfunctionsthateitherreturnthevariableorthrowanerror.Thisfulfilstheobjectaxiomofcategories.

varstr=function(s){

if(typeofs==="string"){

returns;

}

else{

thrownewTypeError("Error:Stringexpected,"+typeofs+"given.");

}

}

varnum=function(n){

if(typeofn==="number"){

returnn;

}

else{

thrownewTypeError("Error:Numberexpected,"+typeofn+"given.");

}

}

varbool=function(b){

if(typeofb==="boolean"){

returnb;

}

else{

thrownewTypeError("Error:Booleanexpected,"+typeofb+"

given.");

}

}

varfunc=function(f){

if(typeoff==="function"){

returnf;

}

else{

thrownewTypeError("Error:Functionexpected,"+typeoff+"

given.");

}

}

However,there’salotofrepeatedcodehereandthatisn’tveryfunctional.Instead,wecancreateafunctionthatreturnsanotherfunctionthatisthetypesafetyfunction.

vartypeOf=function(type){

returnfunction(x){

if(typeofx===type){

returnx;

}

else{

thrownewTypeError("Error:"+type+"expected,"+typeofx+"given.");

}

}

}

varstr=typeOf('string'),

num=typeOf('number'),

func=typeOf('function'),

bool=typeOf('boolean');

Now,wecanusethemtoensurethatourfunctionsbehaveasexpected.

//unprotectedmethod:

varx='24';

x+1;//willreturn'241',not25

//protectedmethod

//plusplus::Int->Int

functionplusplus(n){

returnnum(n)+1;

}

plusplus(x);//throwserror,preferredoverunexpectedoutput

Let’slookatameatierexample.IfwewanttocheckthelengthofaUnixtimestampthatisreturnedbytheJavaScriptfunctionDate.parse(),notasastringbutasanumber,thenwe’llhavetouseourstr()function.

//timestampLength::String->Int

functiontimestampLength(t){returnnum(str(t).length);}

timestampLength(Date.parse('12/31/1999'));//throwserror

timestampLength(Date.parse('12/31/1999')

.toString());//returns12

Functionslikethisthatexplicitlytransformonetypetoanother(ortothesametype)arecalledmorphisms.Thisfulfilsthemorphismaxiomofcategorytheory.TheseforcedtypedeclarationsviathetypesafetyfunctionsandthemorphismsthatusethemareeverythingweneedtorepresentthenotionofacategoryinJavaScript.

ObjectidentitiesThere’soneotherimportantdatatype:objects.

varobj=typeOf('object');

obj(123);//throwserror

obj({x:'a'});//returns{x:'a'}

However,objectsaredifferent.Theycanbeinherited.Everythingthatisnotaprimitive—numbers,strings,Booleans,andfunctions—isanobject,includingarrays,dates,elements,andmore.

There’snowaytoknowwhattypeofobjectsomethingis,asintoknowwhatsub-typeaJavaScript‘object’is,fromthetypeofkeyword,sowe’llhavetoimprovise.ObjectshaveatoString()functionthatwecanhijackforthispurpose.

varobj=function(o){

if(Object.prototype.toString.call(o)==="[objectObject]"){

returno;

}

else{

thrownewTypeError("Error:Objectexpected,somethingelsegiven.");

}

}

Again,withalltheobjectsoutthere,weshouldimplementsomecodere-use.

varobjectTypeOf=function(name){

returnfunction(o){

if(Object.prototype.toString.call(o)==="[object"+name+"]"){

returno;

}

else{

thrownewTypeError("Error:'+name+'expected,somethingelse

given.");

}

}

}

varobj=objectTypeOf('Object');

vararr=objectTypeOf('Array');

vardate=objectTypeOf('Date');

vardiv=objectTypeOf('HTMLDivElement');

Thesewillbeveryusefulforournexttopic:functors.

FunctorsWhilemorphismsaremappingsbetweentypes,functorsaremappingsbetweencategories.Theycanbethoughtofasfunctionsthatliftvaluesoutofacontainer,morphthem,andthenputthemintoanewcontainer.Thefirstinputisamorphismforthetypeandthesecondinputisthecontainer.

NoteThetypesignatureforfunctorslookslikethis:

//myFunctor::(a->b)->fa->fb

Thissays,“givemeafunctionthattakesaandreturnsbandaboxthatcontainsa(s),andI’llreturnaboxthatcontainsb(s).

CreatingfunctorsItturnsoutwealreadyhaveonefunctor:map().Itgrabsthevalueswithinthecontainer,anarray,andappliesafunctiontoit.

[1,4,9].map(Math.sqrt);//Returns:[1,2,3]

However,we’llneedtowriteitasaglobalfunctionandnotasamethodofthearrayobject.Thiswillallowustowritecleaner,safercodelateron.

//map::(a->b)->[a]->[b]

varmap=function(f,a){

returnarr(a).map(func(f));

}

Thisexampleseemslikeacontrivedwrapperbecausewe’rejustpiggybackingontothemap()function.Butitservesapurpose.Itprovidesatemplateformapsofothertypes.

//strmap::(str->str)->str->str

varstrmap=function(f,s){

returnstr(s).split('').map(func(f)).join('');

}

//MyObject#map::(myValue->a)->a

MyObject.prototype.map(f{

returnfunc(f)(this.myValue);

}

ArraysandfunctorsArraysarethepreferredwaytoworkwithdatainfunctionalJavaScript.

Isthereaneasierwaytocreatefunctorsthatarealreadyassignedtoamorphism?Yes,andit’scalledarrayOf.Whenyoupassinamorphismthatexpectsanintegerandreturnsanarray,yougetbackamorphismthatexpectsanarrayofintegersandreturnsanarrayofarrays.

Itisnotafunctoritself,butitallowsustocreatefunctorsfrommorphisms.

//arrayOf::(a->b)->([a]->[b])

vararrayOf=function(f){

returnfunction(a){

returnmap(func(f),arr(a));

}

}

Here’showtocreatefunctorsbyusingmorphism:

varplusplusall=arrayOf(plusplus);//plusplusisourmorphism

console.log(plusplusall([1,2,3]));//returns[2,3,4]

console.log(plusplusall([1,'2',3]));//erroristhrown

TheinterestingpropertyofthearrayOffunctoristhatitworksontypesafetiesaswell.Whenyoupassinthetypesafetyfunctionforstrings,yougetbackatypesafetyfunctionforanarrayofstrings.Thetypesafetiesaretreatedliketheidentityfunctionmorphism.Thiscanbeveryusefulforensuringthatanarraycontainsallthecorrecttypes.

varstrs=arrayOf(str);

console.log(strs(['a','b','c']));//returns['a','b','c']

console.log(strs(['a',2,'c']));//throwserror

Functioncompositions,revisitedFunctionsareanothertypeofprimitivethatwecancreateafunctorfor.Andthatfunctoriscalledfcompose.Wedefinedfunctorsassomethingthattakesavaluefromacontainerandappliesafunctiontoit.Whenthatcontainerisafunction,wejustcallittogetitsinnervalue.

Wealreadyknowwhatfunctioncompositionsare,butlet’slookatwhattheycandoinacategorytheory-drivenenvironment.

Functioncompositionsareassociative.Ifyourhighschoolalgebrateacherwaslikemine,shetaughtyouwhatthepropertyisbutnotwhatitcando.Inpractice,composeiswhattheassociativepropertycando.

Wecandoanyinner-compose,itdoesn’tmatterhowit’sgrouped.Thisisnottobeconfusedwiththecommutativeproperty.ƒogdoesnotalwaysequalgoƒ.Inotherwords,thereverseofthefirstwordofastringisnotthesameasthefirstwordofthereverseofastring.

Whatthisallmeansisthatitdoesn’tmatterwhichfunctionsareappliedandinwhatorder,aslongastheinputofeachfunctionscomesfromtheoutputofthepreviousfunction.Butwait,ifthefunctionontherightreliesonthefunctionontheleft,thencan’ttherebeonlyoneorderofevaluation?Lefttoright?True,butifit’sencapsulated,thenwecancontrolithoweverwefeelfit.ThisiswhatempoweredlazyevaluationinJavaScript.

Let’srewritefunctioncomposition,notasanextensionofthefunctionprototype,butasastand-alonefunctionthatwillallowustogetmoreoutofit.Thebasicformisasfollows:

varfcompose=function(f,g){

returnfunction(){

returnf.call(this,g.apply(this,arguments));

};

};

Butwe’llneedittoworkonanynumberofinputs.

varfcompose=function(){

//firstmakesureallargumentsarefunctions

varfuncs=arrayOf(func)(arguments);

//returnafunctionthatappliesallthefunctions

returnfunction(){

varargsOfFuncs=arguments;

for(vari=funcs.length;i>0;i-=1){

argsOfFuncs=[funcs[i].apply(this,args)];

}

returnargs[0];

};

};

//example:

varf=fcompose(negate,square,mult2,add1);

f(2);//Returns:-36

Nowthatwe’veencapsulatedthefunctions,wehavecontroloverthem.Wecouldrewritethecomposefunctionsuchthateachfunctionacceptsanotherfunctionasinput,storesit,andgivesbackanobjectthatdoesthesame.Insteadofacceptinganarrayasaninput,doingsomethingwithit,andthengivingbackanewarrayforeachoperation,wecanacceptasinglearrayforeachelementinthesource,performalloperationscombined(everymap(),filter(),andsoon,composedtogether),andfinallystoretheresultsinanewarray.Thisislazyevaluationviafunctioncomposition.Noreasontoreinventthewheelhere.Manylibrarieshaveaniceimplementationofthisconcept,includingtheLazy.js,Bacon.jsandwu.jslibraries.

There’salotmorewecandoasaresultofthisdifferentmodel:asynchronousiteration,asynchronouseventhandling,lazyevaluation,andevenautomaticparallelization.

NoteAutomaticparallelization?There’sawordforthatinthecomputerscienceindustry:IMPOSSIBLE.Butisitreallyimpossible?ThenextevolutionaryleapinMoore’slawmightbeacompilerthatparallelizesourcodeforus,andcouldfunctioncompositionbeit?

No,itdoesn’tquiteworkthatway.TheJavaScriptengineiswhatisreallydoingtheparallelization,notautomaticallybutwithwellthought-outcode.Composejustgivestheenginethechancetosplititintoparallelprocesses.Butthatinitselfisprettycool.

MonadsMonadsaretoolsthathelpyoucomposefunctions.

Likeprimitivetypes,monadsarestructuresthatcanbeusedasthecontainersthatfunctors“reachinto”.Thefunctorsgrabthedata,dosomethingtoit,putitintoanewmonad,andreturnit.

Therearethreemonadswe’llfocuson:

MaybesPromisesLenses

Soinadditiontoarrays(map)andfunctions(compose),we’llhavefivefunctors(map,compose,maybe,promiseandlens).Thesearejustsomeofthemanyotherfunctorsandmonadsthatareoutthere.

MaybesMaybesallowustogracefullyworkwithdatathatmightbenullandtohavedefaults.Amaybeisavariablethateitherhassomevalueoritdoesn’t.Anditdoesn’tmattertothecaller.

Onitsown,itmightseemlikethisisnotthatbigadeal.Everybodyknowsthatnull-checksareeasilyaccomplishedwithanif-elsestatement:

if(getUsername()==null){

username='Anonymous'){

else{

username=getUsername();

}

Butwithfunctionalprogramming,we’rebreakingawayfromtheprocedural,line-by-linewayofdoingthingsandinsteadworkingwithpipelinesoffunctionsanddata.Ifwehadtobreakthechaininthemiddlejusttocheckifthevalueexistedornot,wewouldhavetocreatetemporaryvariablesandwritemorecode.Maybesarejusttoolstohelpuskeepthelogicflowingthroughthepipeline.

Toimplementmaybes,we’llfirstneedtocreatesomeconstructors.

//theMaybemonadconstructor,emptyfornow

varMaybe=function(){};

//theNoneinstance,awrapperforanobjectwithnovalue

varNone=function(){};

None.prototype=Object.create(Maybe.prototype);

None.prototype.toString=function(){return'None';};

//nowwecanwritethe`none`function

//savesusfromhavingtowrite`newNone()`allthetime

varnone=function(){returnnewNone()};

//andtheJustinstance,awrapperforanobjectwithavalue

varJust=function(x){returnthis.x=x;};

Just.prototype=Object.create(Maybe.prototype);

Just.prototype.toString=function(){return"Just"+this.x;};

varjust=function(x){returnnewJust(x)};

Finally,wecanwritethemaybefunction.Itreturnsanewfunctionthateitherreturnsnothingoramaybe.Itisafunctor.

varmaybe=function(m){

if(minstanceofNone){

returnm;

}

elseif(minstanceofJust){

returnjust(m.x);

}

else{

thrownewTypeError("Error:JustorNoneexpected,"+m.toString()+"

given.");

}

}

Andwecanalsocreateafunctorgeneratorjustlikewedidwitharrays.

varmaybeOf=function(f){

returnfunction(m){

if(minstanceofNone){

returnm;

}

elseif(minstanceofJust){

returnjust(f(m.x));

}

else{

thrownewTypeError("Error:JustorNoneexpected,"+m.toString()+

"given.");

}

}

}

SoMaybeisamonad,maybeisafunctor,andmaybeOfreturnsafunctorthatisalreadyassignedtoamorphism.

We’llneedonemorethingbeforewecanmoveforward.We’llneedtoaddamethodtotheMaybemonadobjectthathelpsususeitmoreintuitively.

Maybe.prototype.orElse=function(y){

if(thisinstanceofJust){

returnthis.x;

}

else{

returny;

}

}

Initsrawform,maybescanbeuseddirectly.

maybe(just(123)).x;//Returns123

maybeOf(plusplus)(just(123)).x;//Returns124

maybe(plusplus)(none()).orElse('none');//returns'none'

Anythingthatreturnsamethodthatisthenexecutediscomplicatedenoughtobebeggingfortrouble.Sowecanmakeitalittlecleanerbycallingonourcurry()function.

maybePlusPlus=maybeOf.curry()(plusplus);

maybePlusPlus(just(123)).x;//returns123

maybePlusPlus(none()).orElse('none');//returnsnone

Buttherealpowerofmaybeswillbecomeclearwhenthedirtybusinessofdirectlycallingthenone()andjust()functionsisabstracted.We’lldothiswithanexampleobjectUser,thatusesmaybesfortheusername.

varUser=function(){

this.username=none();//initiallysetto`none`

};

User.prototype.setUsername=function(name){

this.username=just(str(name));//it'snowa`just

};

User.prototype.getUsernameMaybe=function(){

varusernameMaybe=maybeOf.curry()(str);

returnusernameMaybe(this.username).orElse('anonymous');

};

varuser=newUser();

user.getUsernameMaybe();//Returns'anonymous'

user.setUsername('Laura');

user.getUsernameMaybe();//Returns'Laura'

Andnowwehaveapowerfulandsafewaytodefinedefaults.KeepthisUserobjectinmindbecausewe’llbeusingitlateroninthischapter.

PromisesThenatureofpromisesisthattheyremainimmunetochangingcircumstances.

-FrankUnderwood,HouseofCards

Infunctionalprogramming,we’reoftenworkingwithpipelinesanddataflows:chainsoffunctionswhereeachfunctionproducesadatatypethatisconsumedbythenext.However,manyofthesefunctionsareasynchronous:readFile,events,AJAX,andsoon.Insteadofusingacontinuation-passingstyleanddeeplynestedcallbacks,howcanwemodifythereturntypesofthesefunctionstoindicatetheresult?Bywrappingtheminpromises.

Promisesarelikethefunctionalequivalentofcallbacks.Obviously,callbacksarenotallthatfunctionalbecause,ifmorethanonefunctionismutatingthesamedata,thentherecanberaceconditionsandbugs.Promisessolvethatproblem.

Youshouldusepromisestoturnthis:

fs.readFile("file.json",function(err,val){

if(err){

console.error("unabletoreadfile");

}

else{

try{

val=JSON.parse(val);

console.log(val.success);

}

catch(e){

console.error("invalidjsoninfile");

}

}

});

Intothefollowingcodesnippet:

fs.readFileAsync("file.json").then(JSON.parse)

.then(function(val){

console.log(val.success);

})

.catch(SyntaxError,function(e){

console.error("invalidjsoninfile");

})

.catch(function(e){

console.error("unabletoreadfile")

});

TheprecedingcodeisfromtheREADMEforbluebird:afullfeaturedPromises/A+implementationwithexceptionallygoodperformance.Promises/A+isaspecificationforimplementingpromisesinJavaScript.GivenitscurrentdebatewithintheJavaScriptcommunity,we’llleavetheimplementationsuptothePromises/A+team,asitismuchmorecomplexthanmaybes.

Buthere’sapartialimplementation:

//thePromisemonad

varPromise=require('bluebird');

//thepromisefunctor

varpromise=function(fn,receiver){

returnfunction(){

varslice=Array.prototype.slice,

args=slice.call(arguments,0,fn.length-1),

promise=newPromise();

args.push(function(){

varresults=slice.call(arguments),

error=results.shift();

if(error)promise.reject(error);

elsepromise.resolve.apply(promise,results);

});

fn.apply(receiver,args);

returnpromise;

};

};

Nowwecanusethepromise()functortotransformfunctionsthattakecallbacksintofunctionsthatreturnpromises.

varfiles=['a.json','b.json','c.json'];

readFileAsync=promise(fs.readFile);

vardata=files

.map(function(f){

readFileAsync(f).then(JSON.parse)

})

.reduce(function(a,b){

return$.extend({},a,b)

});

LensesAnotherreasonwhyprogrammersreallylikemonadsisthattheymakewritinglibrariesveryeasy.Toexplorethis,let’sextendourUserobjectwithmorefunctionsforgettingandsettingvaluesbut,insteadofusinggettersandsetters,we’lluselenses.

Lensesarefirst-classgettersandsetters.Theyallowustonotjustgetandsetvariables,butalsotorunfunctionsoverit.Butinsteadofmutatingthedata,theycloneandreturnthenewdatamodifiedbythefunction.Theyforcedatatobeimmutable,whichisgreatforsecurityandconsistencyaswellforlibraries.They’regreatforelegantcodenomatterwhattheapplication,solongastheperformance-hitofintroducingadditionalarraycopiesisnotacriticalissue.

Beforewewritethelens()function,let’slookathowitworks.

varfirst=lens(

function(a){returnarr(a)[0];},//get

function(a,b){return[b].concat(arr(a).slice(1));}//set

);

first([1,2,3]);//outputs1

first.set([1,2,3],5);//outputs[5,2,3]

functiontenTimes(x){returnx*10}

first.modify(tenTimes,[1,2,3]);//outputs[10,2,3]

Andhere’showthelens()functionworks.Itreturnsafunctionwithget,setandmoddefined.Thelens()functionitselfisafunctor.

varlens=fuction(get,set){

varf=function(a){returnget(a)};

f.get=function(a){returnget(a)};

f.set=set;

f.mod=function(f,a){returnset(a,f(get(a)))};

returnf;

};

Let’stryanexample.We’llextendourUserobjectfromthepreviousexample.

//userName::User->str

varuserName=lens(

function(u){returnu.getUsernameMaybe()},//get

function(u,v){//set

u.setUsername(v);

returnu.getUsernameMaybe();

}

);

varbob=newUser();

bob.setUsername('Bob');

userName.get(bob);//returns'Bob'

userName.set(bob,'Bobby');//return'Bobby'

userName.get(bob);//returns'Bobby'

userName.mod(strToUpper,bob);//returns'BOBBY'

strToUpper.compose(userName.set)(bob,'robert');//returns'ROBERT'

userName.get(bob);//returns'robert'

jQueryisamonadIfyouthinkallthisabstractbabbleaboutcategories,functors,andmonadshasnoreal-worldapplication,thinkagain.jQuery,thepopularJavaScriptlibrarythatprovidesanenhancedinterfaceforworkingwithHTMLis,in-fact,amonadiclibrary.

ThejQueryobjectisamonadanditsmethodsarefunctors.Really,they’reaspecialtypeoffunctorcalledendofunctors.Endofunctorsarefunctorsthatreturnthesamecategoryastheinput,thatis,F::X->X.EachjQuerymethodtakesajQueryobjectandreturnsajQueryobject,whichallowsmethodstobechained,andtheywillhavethetypesignaturejFunc::jquery-obj->jquery-obj.

$('li').add('p.me-too').css('color','red').attr({id:'foo'});

ThisisalsowhatempowersjQuery’spluginframework.IftheplugintakesajQueryobjectasinputandreturnsoneasoutput,thenitcanbeinsertedintothechain.

Let’slookathowjQuerywasabletoimplementthis.

Monadsarethecontainersthatthefunctors“reachinto”togetthedata.Inthisway,thedatacanbeprotectedandcontrolledbythelibrary.jQueryprovidesaccesstotheunderlyingdata,awrappedsetofHTMLelements,viaitsmanymethods.

ThejQueryobjectitselfiswrittenastheresultofananonymousfunctioncall.

varjQuery=(function(){

varj=function(selector,context){

varjq-obj=newj.fn.init(selector,context);

returnjq-obj;

};

j.fn=j.prototype={

init:function(selector,context){

if(!selector){

returnthis;

}

}

};

j.fn.init.prototype=j.fn;

returnj;

})();

InthishighlysimplifiedversionofjQuery,itreturnsafunctionthatdefinesthejobject,whichisactuallyjustanenhancedinitconstructor.

var$=jQuery();//thefunctionisreturnedandassignedto`$`

varx=$('#select-me');//jQueryobjectisreturned

Inthesamewaythatfunctorsliftvaluesoutofacontainer,jQuerywrapstheHTMLelementsandprovidesaccesstothemasopposedtomodifyingtheHTMLelementsdirectly.

jQuerydoesn’tadvertisethisoften,butithasitsownmap()methodforliftingtheHTMLelementobjectsoutofthewrapper.Justlikethefmap()method,theelementsarelifted,

somethingisdonewiththem,andthenthey’replacedbackintothecontainer.ThisishowmanyofjQuery’scommandsworkinthebackend.

$('li').map(function(index,element){

//dosomethingtotheelement

returnelement

});

AnotherlibraryforworkingwithHTMLelements,Prototype,doesnotworklikethis.PrototypealterstheHTMLelementsdirectlyviahelpers.Consequently,ithasnotfairedaswellintheJavaScriptcommunity.

ImplementingcategoriesIt’sabouttimeweformallydefinedcategorytheoryasJavaScriptobjects.Categoriesareobjects(types)andmorphisms(functionsthatonlyworkonthosetypes).It’sanextremelyhigh-level,totally-declarativewaytoprogram,butitensuresthatthecodeisextremelysafeandreliable—perfectforAPIsandlibrariesthatareworriedaboutconcurrencyandtypesafety.

First,we’llneedafunctionthathelpsuscreatemorphisms.We’llcallithomoMorph()becausethey’llbehomomorphisms.Itwillreturnafunctionthatexpectsafunctiontobepassedinandproducesthecompositionofit,basedontheinputs.Theinputsarethetypesthatthemorphismacceptsasinputandgivesasoutput.Justlikeourtypesignatures,thatis,//morph::num->num->[num],onlythelastoneistheoutput.

varhomoMorph=function(/*input1,input2,...,inputN,output*/){

varbefore=checkTypes(arrayOf(func)

(Array.prototype.slice.call(arguments,0,arguments.length-1)));

varafter=func(arguments[arguments.length-1])

returnfunction(middle){

returnfunction(args){

returnafter(middle.apply(this,before([].slice.apply(arguments))));

}

}

}

//nowwedon'tneedtoaddtypesignaturecomments

//becausenowthey'rebuiltrightintothefunctiondeclaration

add=homoMorph(num,num,num)(function(a,b){returna+b})

add(12,24);//returns36

add('a','b');//throwserror

homoMorph(num,num,num)(function(a,b){

returna+b;

})(18,24);//returns42

ThehomoMorph()functionisfairlycomplex.Itusesaclosure(seeChapter2,FundamentalsofFunctionalProgramming)toreturnafunctionthatacceptsafunctionandchecksitsinputandoutputvaluesfortypesafety.Andforthat,itreliesonahelperfunction:checkTypes,whichisdefinedasfollows:

varcheckTypes=function(typeSafeties){

arrayOf(func)(arr(typeSafeties));

varargLength=typeSafeties.length;

returnfunction(args){

arr(args);

if(args.length!=argLength){

thrownewTypeError('Expected'+argLength+'arguments');

}

varresults=[];

for(vari=0;i<argLength;i++){

results[i]=typeSafeties[i](args[i]);

}

returnresults;

}

}

Nowlet’sformallydefinesomehomomorphisms.

varlensHM=homoMorph(func,func,func)(lens);

varuserNameHM=lensHM(

function(u){returnu.getUsernameMaybe()},//get

function(u,v){//set

u.setUsername(v);

returnu.getUsernameMaybe();

}

)

varstrToUpperCase=homoMorph(str,str)(function(s){

returns.toUpperCase();

});

varmorphFirstLetter=homoMorph(func,str,str)(function(f,s){

returnf(s[0]).concat(s.slice(1));

});

varcapFirstLetter=homoMorph(str,str)(function(s){

returnmorphFirstLetter(strToUpperCase,s)

});

Finally,wecanbringitonhome.Thefollowingexampleincludesfunctioncomposition,lenses,homomorphisms,andmore.

//homomorphiclenses

varbill=newUser();

userNameHM.set(bill,'William');//Returns:'William'

userNameHM.get(bill);//Returns:'William'

//compose

varcapatolizedUsername=fcompose(capFirstLetter,userNameHM.get);

capatolizedUsername(bill,'bill');//Returns:'Bill'

//it'sagoodideatousehomoMorphon.setand.gettoo

vargetUserName=homoMorph(obj,str)(userNameHM.get);

varsetUserName=homoMorph(obj,str,str)(userNameHM.set);

getUserName(bill);//Returns:'Bill'

setUserName(bill,'Billy');//Returns:'Billy'

//nowwecanrewritecapatolizeUsernamewiththenewsetter

capatolizedUsername=fcompose(capFirstLetter,setUserName);

capatolizedUsername(bill,'will');//Returns:'Will'

getUserName(bill);//Returns:'will'

Theprecedingcodeisextremelydeclarative,safe,reliable,anddependable.

NoteWhatdoesitmeanforcodetobedeclarative?Inimperativeprogramming,wewritesequencesofinstructionsthattellthemachinehowtodowhatwewant.Infunctionalprogramming,wedescriberelationshipsbetweenvaluesthattellthemachinewhatwewantittocompute,andthemachinefiguresouttheinstructionsequencestomakeithappen.Functionalprogrammingisdeclarative.

EntirelibrariesandAPIscanbeconstructedthiswaythatallowprogrammerstowritecodefreelywithoutworryingaboutconcurrencyandtypesafetybecausethoseworriesarehandledinthebackend.

SummaryAboutoneinevery2,000peoplehasaconditionknownassynesthesia,aneurologicalphenomenoninwhichonesensoryinputbleedsintoanother.Themostcommonforminvolvesassigningcolorswithletters.However,thereisanevenrarerformwheresentencesandparagraphsareassociatedwithtastesandfeelings.

Forthesepeople,theydon’treadwordbyword,sentencebysentence.Theylookatthewholepage/document/programandgetasenseforhowittastes—notinthemouthbutinthemind.Thentheyputthepartsofthetexttogetherlikethepiecesofapuzzle.

Thisiswhatitisliketowritefullydeclarativecode:codethatdescribestherelationshipsbetweenvaluesthattellsthemachinewhatwewantittocompute.Thepartsoftheprogramarenotinstructionsinline-by-lineorder.Synestheticsmaybeabletodoitnaturally,butwithalittlepracticeanyonecanlearnhowtoputtherelationalpuzzlepiecestogether.

Inthischapter,welookedatseveralmathematicalconceptsthatapplytofunctionalprogrammingandhowtheyallowustobuildrelationshipsbetweendata.Next,we’llexplorerecursionandotheradvancedtopicsinJavaScript.

Chapter6.AdvancedTopicsandPitfallsinJavaScriptJavaScripthasbeencalledthe“assemblylanguageoftheweb”.Theanalogy(itisn’tperfect,butwhichanalogyis?)drawsfromthefactthatJavaSciptisoftenatargetforcompilation,namelyfromClojureandCoffeeScript,butalsofrommanyothersourcessuchaspyjamas(pythontoJS)andGoogleWebKit(JavatoJS).

ButtheanalogyalsoreferencesthefoolishideathatJavaScriptisasexpressiveandlow-levelasx86assembly.PerhapsthisnotionstemsfromthefactthatJavaScripthasbeenbashedforitsdesignflawsandoversightseversinceitwasfirstshippedwithNetscapebackin1995.Itwasdevelopedandreleasedinahurry,beforeitcouldbefullydeveloped.Andbecauseofthat,somequestionabledesignchoicesmadeitswayintoJavaScript,thelanguagethatsoonbecamethede-factoscriptinglanguageoftheweb.Semicolonswereabigmistake.Sowereitsambiguousmethodsfordefiningfunctions.Isitvarfoo=function();orfunctionfoo();?

Functionalprogrammingisanexcellentwaytoside-stepsomeofthesemistakes.ByfocusingonthefactthatJavaScriptistrulyafunctionallanguage,itbecomesclearthat,intheprecedingexampleaboutthedifferentwaystodeclareafunction,it’sbesttodeclarefunctionsasvariables.AndthatsemicolonsaremostlyjustsyntacticsugartomakeJavaScriptappearmoreC-like.

Butalwaysrememberthelanguageyouareworkingwith.JavaScript,likeanyotherlanguage,hasitspitfalls.And,whenprogramminginastylethatoftenskirtsthebleedingedgeofwhat’spossible,thoseminorstumblescanbecomenon-recoverablegotchas.Someofthesegotchasinclude:

RecursionVariablescopeandclosuresFunctiondeclarationsvs.functionexpressions

However,theseissuescanbeovercomewithalittleattention.

RecursionRecursionisveryimportanttofunctionalprogramminginanylanguage.Manyfunctionallanguagesgosofarastorequirerecursionforiterationbynotprovidingforandwhileloopstatements;thisisonlypossiblewhentail-calleliminationisguaranteedbythelanguage,whichisnotthecaseforJavaScript.AquickprimeronrecursionwasgiveninChapter2,FundamentalsofFunctionalProgramming.Butinthissection,we’lldigdeeperintoexactlyhowrecursionworksinJavaScript.

TailrecursionJavaScript’sroutineforhandlingrecursionisknownastailrecursion,astack-basedimplementationofrecursion.Thismeansthat,foreveryrecursivecall,thereisanewframeinthestack.

Toillustratetheproblemsthatcanarisefromthismethod,let’susetheclassicrecursivealgorithmforfactorials.

varfactorial=function(n){

if(n==0){

//basecase

return1;

}

else{

//recursivecase

returnn*factorial(n-1);

}

}

Thealgorithmwillcallitselfntimestogettheanswer.It’sliterallycomputing(1x1x2x3x…xN).ThatmeansthetimecomplexityisO(n).

NoteO(n),pronounced“bigohtothen,”meansthatthecomplexityofthealgorithmwillgrowatarateofnasthesizeoftheinputgrows,whichisleanergrowth.O(n2)isexponentialgrowth,O(log(n))islogarithmicgrowth,andsoon.Thisnotationcanbeusedfortimecomplexityaswellasspacecomplexity.

But,becauseanewframeinthememorystackisallocatedforeachiteration,thespacecomplexityisalsoO(n).Thisisaproblem.Thismeansthatmemorywillbeconsumedatsucharatethememorylimitwillbeexceededfartooeasily.Onmylaptop,factorial(23456)returnsUncaughtError:RangeError:Maximumcallstacksizeexceeded.

Whilecalculatingthefactorialof23,456isafrivolousendeavor,youcanbeassuredthatmanyproblemsthataresolvedwithrecursionwillgrowtothatsizewithouttoomuchtrouble.Considerthecaseofdatatrees.Thetreecouldbeanything:searchapplications,filesystems,routingtables,andsoon.Belowisaverysimpleimplementationofthetreetraversalfunction:

vartraverse=function(node){

node.doSomething();//whateverworkneedstobedone

node.childern.forEach(traverse);//manyrecursivecalls

}

Withjusttwochildrenpernode,bothtimecomplexityandspacecomplexity,(intheworstcase,wheretheentiretreemustbetraversedtofindtheanswer),wouldbeO(n2)becausetherewouldbetworecursivecallseach.Withmanychildrenpernode,thecomplexitywouldbeO(nm)wheremisthenumberofchildren.Andrecursionisthepreferredalgorithmfortreetraversal;awhileloopwouldbemuchmorecomplexandwouldrequire

themaintenanceofastack.

ExponentialgrowthlikethiswouldmeanthatitwouldnottakeaverylargetreetothrowaRangeErrorexception.Theremustbeabetterway.

TheTail-calleliminationWeneedawaytoeliminatetheallocationofnewstackframesforeveryrecursivecall.Thisisknownastail-callelimination.

Withtail-callelimination,whenafunctionreturnstheresultofcallingitself,thelanguagedoesn’tactuallyperformanotherfunctioncall.Itturnsthewholethingintoaloopforyou.

OK,sohowdowedothis?Withlazyevaluation.Ifwecouldrewriteittofoldoveralazysequence,suchthatthefunctionreturnsavalueoritreturnstheresultofcallinganotherfunctionwithoutdoinganythingwiththatresult,thennewstackframesdon’tneedtobeallocated.

Toputitin“tailrecursionform”,thefactorialfunctionwouldhavetoberewrittensuchthattheinnerprocedurefactcallsitselflastinthecontrolflow,asshowninthefollowingcodesnippet:

varfactorial=function(n){

var_fact=function(x,n){

if(n==0){

//basecase

returnx;

}

else{

//recursivecase

return_fact(n*x,n-1);

}

}

returnfact(1,n);

}

NoteInsteadofhavingtheresultproducedbythefirstfunctionintherecursiontail(likeinn*factorial(n-1)),theresultiscomputedgoingdowntherecursiontail(withthecallto_fact(r*n,n-1))andisproducedbythelastfunctioninthistail(withreturnr;).Thecomputationgoesonlyonewaydown,notonitswayup.It’srelativelyeasytoprocessitasaniterationfortheinterpreter.

However,tail-calleliminationdoesnotworkinJavaScript.PuttheabovecodeintoyourfavoriteJavaScriptengineandfactorial(24567)stillreturnsUncaughtError:RangeError:Maximumcallstacksizeexceededexception.Tail-calleliminationislistedasanewfeaturetobeincludedinthenextreleaseofECMAScript,butitwillbesometimebeforeallbrowsersimplementit.

JavaScriptcannotoptimizefunctionsthatareputintotailrecursionform.It’safeatureofthelanguagespecificationandruntimeinterpreter,plainandsimple.Ithastodowithhowtheinterpreteracquiresresourcesforstackframes.Somelanguageswillreusethesame

stackframewhenitdoesn’tneedtorememberanythingnew,likeintheprecedingfunction.Thisishowtail-calleliminationreducesbothtimeandspacecomplexity.

Unfortunately,JavaScriptdoesnotdothis.Butifitdid,itwouldreorganizethestackframesfromthis:

callfactorial(3)

callfact(31)

callfact(23)

callfact(16)

callfact(06)

return6

return6

return6

return6

return6

intothefollowing:

callfactorial(3)

callfact(31)

callfact(23)

callfact(16)

callfact(06)

return6

return6

TrampoliningThesolution?Aprocessknownastrampolining.It’sawayto“hack”theconceptoftail-calleliminationintoaprogrambyusingthunks.

NoteThunksare,forthispurpose,expressionswithargumentsthatwrapanonymousfunctionswithnoargumentsoftheirown.Forexample:function(str){returnfunction(){console.log(str)}}.Thispreventstheexpressionfrombeingevaluateduntilareceivingfunctioncallstheanonymousfunction.

Atrampolineisafunctionthattakesafunctionasinputandrepeatedlyexecutesitsreturnedvalueuntilsomethingotherthanafunctionisreturned.Asimpleimplementationisshowninthefollowingcodesnippet:

vartrampoline=function(f){

while(f&&finstanceofFunction){

f=f.apply(f.context,f.args);

}

returnf;

}

Toactuallyimplementtail-callelimination,weneedtousethunks.Forthis,wecanusethebind()functionthatallowsustoapplyamethodtooneobjectwiththethiskeywordassignedtoanother.Internally,it’sthesameasthecallkeyword,butit’schainedtothemethodandreturnsanewboundfunction.Thebind()functionactuallydoespartialapplication,thoughinaverylimitedway.

varfactorial=function(n){

var_fact=function(x,n){

if(n==0){

//basecase

returnx;

}

else{

//recursivecase

return_fact.bind(null,n*x,n-1);

}

}

returntrampoline(_fact.bind(null,1,n));

}

Butwritingthefact.bind(null,...)methodiscumbersomeandwouldconfuseanybodyreadingthecode.Instead,let’swriteourownfunctionforcreatingthunks.Thereareafewthingsthethunk()functionmustdo:

thunk()functionmustemulatethe_fact.bind(null,n*x,n-1)methodthatreturnsanon-evaluatedfunctionThethunk()functionshouldenclosetwomorefunctions:

Forprocessingthegivefunction,andForprocessingthefunctionargumentsthatwillbeusedwhenthegivenfunction

isinvoked

Withthat,we’rereadytowritethefunction.Weonlyneedafewlinesofcodetowriteit.

varthunk=function(fn){

returnfunction(){

varargs=Array.prototype.slice.apply(arguments);

returnfunction(){returnfn.apply(this,args);};

};

};

Nowwecanusethethunk()functioninourfactorialalgorithmlikethis:

varfactorial=function(n){

varfact=function(x,n){

if(n==0){

returnx;

}

else{

returnthunk(fact)(n*x,n-1);

}

}

returntrampoline(thunk(fact)(1,n));

}

Butagain,wecansimplifyitjustabitfurtherbydefiningthe_fact()functionasathunk()function.Bydefiningtheinnerfunctionasathunk()function,we’rerelievedofhavingtousethethunk()functionbothinsidetheinnerfunctiondefinitionandinthereturnstatement.

varfactorial=function(n){

var_fact=thunk(function(x,n){

if(n==0){

//basecase

returnx;

}

else{

//recursivecase

return_fact(n*x,n-1);

}

});

returntrampoline(_fact(1,n));

}

Theresultisbeautiful.Whatseemslikethefunction_fact()beingrecursivelycalledforatail-freerecursionisalmosttransparentlyprocessedasaniteration!

Finally,let’sseehowthetrampoline()andthunk()functionsworkwithourmoremeaningfulexampleoftreetraversal.Thefollowingisacrudeexampleofhowadatatreecouldbetraversedusingtrampoliningandthunks:

vartreeTraverse=function(trunk){

var_traverse=thunk(function(node){

node.doSomething();

node.children.forEach(_traverse);

}

trampoline(_traverse(trunk));

}

We’vesolvedtheissueoftailrecursion.Butisthereanevenbetterway?Whatifwecouldsimplyconverttherecursivefunctiontoanon-recursivefunction?Upnext,we’lllookathowtodojustthat.

TheY-combinatorTheY-combinatorisoneofthosethingsincomputersciencethatamazeeventhedeftestofprogrammingmasterminds.Itsabilitytoautomaticallyconvertrecursivefunctionstonon-recursivefunctionsiswhyDouglasCrockfordcallsit“oneofthemoststrangeandwonderfulartifactsofcomputerscience”,andSussmanandSteeleoncesaid,“Thatthismanagestoworkistrulyremarkable”.

Soatruly-remarkable,wonderfullystrangeartifactofcomputersciencethatbringsrecursivefunctionstotheirkneesmustbemassiveandcomplex,right?No,notexactly.ItsimplementationinJavaScriptisonlynine,veryodd,linesofcode.Theyareasfollows:

varY=function(F){

return(function(f){

returnf(f);

}(function(f){

returnF(function(x){

returnf(f)(x);

});

}));

}

Here’showitworks:itfindsthe“fixedpoint”ofthefunctionpassedinasanargument.Fixedpointsofferanotherwaytothinkaboutfunctionsratherthanrecursionanditerationinthetheoryofcomputerprogramming.Anditdoesthiswithonlytheuseofanonymousfunctionexpressions,functionapplications,andvariablereferences.NotethatYdoesnotreferenceitself.Infact,allthosefunctionsareanonymous.

Asyoumighthaveguessed,theY-combinatorcameoutoflambdacalculus.It’sactuallyderivedwiththehelpofanothercombinatorcalledtheU-combinator.Combinatorsarespecialhigher-orderfunctionsthatonlyusefunctionapplicationandearlierdefinedcombinatorstodefinearesultfromitsinput.

TodemonstratetheY-combinator,we’llagainturntothefactorialproblem,butweneedtodefinethefactorialfunctionalittledifferently.Insteadofwritingarecursivefunction,wewriteafunctionthatreturnsafunctionthatisthemathematicaldefinitionoffactorials.ThenwecanpassthisintotheY-combinator.

varFactorialGen=function(factorial){

return(function(n){

if(n==0){

//basecase

return1;

}

else{

//recursivecase

returnn*factorial(n–1);

}

});

};

Factorial=Y(FactorialGen);

Factorial(10);//3628800

However,whenwegiveitasignificantlylargenumber,thestackoverflowsjustasiftailrecursionwithouttrampoliningwasused.

Factorial(23456);//RangeError:Maximumcallstacksizeexceeded

ButwecanusetrampoliningwiththeY-combinatorasinthefollowing:

varFactorialGen2=function(factorial){

returnfunction(n){

varfactorial=thunk(function(x,n){

if(n==0){

returnx;

}

else{

returnfactorial(n*x,n-1);

}

});

returntrampoline(factorial(1,n));

}

};

varFactorial2=Y(FactorialGen2)

Factorial2(10);//3628800

Factorial2(23456);//Infinity

WecanalsorearrangetheY-combinatortoperformsomethingcalledmemoization.

MemoizationMemoizationisthetechniqueofstoringtheresultofexpensivefunctioncalls.Whenthefunctionislatercalledwiththesamearguments,thestoredresultisreturnedratherthancomputingtheresultagain.

AlthoughtheY-combinatorismuchfasterthanrecursion,itisstillrelativelyslow.Tospeeditup,wecancreateamemoizingfixed-pointcombinator:aY-likecombinatorthatcachestheresultsofintermediatefunctioncalls.

varYmem=function(F,cache){

if(!cache){

cache={};//Createanewcache.

}

returnfunction(arg){

if(cache[arg]){

//Answerincache

returncache[arg];

}

//elsecomputetheanswer

varanswer=(F(function(n){

return(Ymem(F,cache))(n);

}))(arg);//Computetheanswer.

cache[arg]=answer;//Cachetheanswer.

returnanswer;

};

}

Sohowmuchfasterisit?Byusinghttp://jsperf.com/,wecancomparetheperformance.

Thefollowingresultsarewithrandomnumbersbetween1and100.WecanseethatthememoizingY-combinatorismuch,muchfaster.Andaddingtrampoliningtoitdoesnotslowitdownbymuch.YoucanviewtheresultsandrunthetestsyourselfatthisURL:http://jsperf.com/memoizing-y-combinator-vs-tail-call-optimization/7.

Thebottomlineis:themostefficientandsafestmethodofperformingrecursioninJavaScriptistousethememoizingY-combinatorwithtail-calleliminationviatrampoliningandthunks.

VariablescopeThescopeofvariablesinJavaScriptisnotnatural.Infact,sometimesit’sdownrightcounter-intuitive.TheysaythatJavaScriptprogrammerscanbejudgedbyhowwelltheyunderstandscope.

ScoperesolutionsFirst,let’sgooverthedifferentscoperesolutionsinJavaScript.

JavaScriptusesscopechainstoestablishthescopeofvariables.Whenresolvingavariable,itstartsattheinnermostscopeandsearchesoutwards.

GlobalscopeVariables,functions,andobjectsdefinedatthislevelareavailabletoanycodeintheentireprogram.Thisistheoutermostscope.

varx='hi';

functiona(){

console.log(x);

}

a();//'hi'

LocalscopeEachfunctiondescribedhasitsownlocalscope.Anyfunctiondefinedwithinanotherfunctionhasanestedlocalscopethatislinkedtotheouterfunction.Almostalways,it’sthepositioninthesourcethatdefinesthescope.

varx='hi';

functiona(){

console.log(x);

}

functionb(){

varx='hello';

console.log(x);

}

b();//hello

a();//hi

Localscopeisonlyforfunctionsandnotforanyexpressionstatements(if,for,while,andsoon),whichisdifferentfromhowmostlanguagestreatscope.

functionc(){

vary='greetings';

if(true){

vary='gutentag';

}

console.log(y);

}

functiond(){

vary='greetings';

functione(){

vary='gutentag';

}

console.log(y)

}

c();//'gutentag'

d();//'greetings'

Infunctionalprogramming,thisisn’tasmuchofaconcernbecausefunctionsareusedmoreoftenandexpressionstatementslessoften.Forexample:

functione(){

varz='namaste';

[1,2,3].foreach(function(n){

varz='aloha';

}

isTrue(function(){

varz='goodmorning';

});

console.log(z);

}

e();//'namaste'

ObjectpropertiesObjectpropertieshavetheirownscopechainsaswell.

varx='hi';

varobj=function(){

this.x='hola';

};

varfoo=newobj();

console.log(foo.x);//'hola'

foo.x='bonjour';

console.log(foo.x);//'bonjour'

Theobject’sprototypeisfurtherdownthescopechain.

obj.prototype.x='greetings';

obj.prototype.y='konnichiha';

varbar=newobj();

console.log(bar.x);//stillprints'hola'

console.log(bar.y);//'konnichiha'

Thisisn’tevenclosetobeingcomprehensive,butthesethreetypesofscopeareenoughtogetstarted.

ClosuresOneproblemwiththisscopestructureisthatitleavesnoroomforprivatevariables.Considerthefollowingcodesnippet:

varname='FordFocus';

varyear='2006';

varmillage=123456;

functiongetMillage(){

returnmillage;

}

functionupdateMillage(n){

millage=n;

}

Thesevariablesandfunctionsareglobal,whichmeansitwouldbetooeasyforcodelaterdowntheprogramtoaccidentallyoverwritethem.Onesolutionwouldbetoencapsulatethemintoafunctionandcallthatfunctionimmediatelyafterdefiningit.

varcar=function(){

varname='FordFocus';

varyear='2006';

varmillage=123456;

functiongetMillage(){

returnMillage;

}

functionupdateMillage(n){

millage=n;

}

}();

Nothingishappeningoutsidethefunction,soweoughttodiscardthefunctionnamebymakingitanonymous.

(function(){

varname='FordFocus';

varyear='2006';

varmillage=123456;

functiongetMillage(){

returnmillage;

}

functionupdateMillage(n){

millage=n;

}

})();

TomakethefunctionsgetValue()andupdateMillage()availableoutsidetheanonymousfunction,we’llneedtoreturntheminanobjectliteralasshowninthefollowingcodesnippet:

varcar=function(){

varname='FordFocus';

varyear='2006';

varmillage=123456;

return{

getMillage:function(){

returnmillage;

},

updateMillage:function(n){

millage=n;

}

}

}();

console.log(car.getMillage());//works

console.log(car.updateMillage(n));//alsoworks

console.log(car.millage);//undefined

Thisgivesuspseudo-privatevariables,buttheproblemsdon’tstopthere.ThefollowingsectionexploresmoreissueswithvariablescopeinJavaScript.

GotchasManyvariablescopenuancescanbefoundthroughoutJavaScript.Thefollowingisbynomeansacomprehensivelist,butitcoversthemostcommoncases:

Thefollowingwilloutput4,not‘undefined’asonewouldexpect:

for(varn=4;false;){}console.log(n);

Thisisduetothefactthat,inJavaScript,variabledefinitionhappensatthebeginningofthecorrespondingscope,notjustwhenitisdeclared.

Ifyoudefineavariableintheouterscope,andthenhaveanifstatementdefineavariableinsidethefunctionwiththesamename,evenifthatifbranchisn’treached,itisredefined.Anexample:

varx=1;

functionfoo(){

if(false){

varx=2;

}

returnx;

}

foo();//Returnvalue:'undefined',expectedreturnvalue:

2

Again,thisiscausedbymovingthevariabledefinitionatthebeginningofthescopewiththeundefinedvalue.

Inthebrowser,globalvariablesarereallystoredinthewindowobject.

window.a=19;

console.log(a);//Output:19

aintheglobalscopemeansaasanattributeofthecurrentcontext,soa===this.aandwindowobjectinabrowseractasanequivalentofthethiskeywordintheglobalscope.

ThefirsttwoexamplesarearesultofafeatureofJavaScriptknownashoisting,whichwillbeacriticalconceptinthenextsectionaboutwritingfunctions.

FunctiondeclarationsversusfunctionexpressionsversusthefunctionconstructorWhatisthedifferencebetweenthesethreestatements?

functionfoo(n){returnn;}

varfoo=function(n){returnn;};

varfoo=newFunction('n','returnn');

Atfirstglance,they’remerelydifferentwaystowritethesamefunction.Butthere’salittlemoregoingonhere.Andifwe’retotakefulladvantageoffunctionsinJavaScriptinordertomanipulatethemintoafunctionalprogrammingstyle,thenwe’dbetterbeabletogetthisright.Ifthereisabetterwaytodosomethingincomputerprogramming,thenthatonewayshouldbetheonlyway.

FunctiondeclarationsFunctiondeclarations,sometimescalledfunctionstatements,defineafunctionbyusingthefunctionkeyword.

functionfoo(n){

returnn;

}

Functionsthataredeclaredwiththissyntaxarehoistedtothetopofthecurrentscope.Whatthisactuallymeansisthat,evenifthefunctionisdefinedseverallinesdown,JavaScriptknowsaboutitandcanuseitearlierinthescope.Forexample,thefollowingwillcorrectlyprint6totheconsole:

foo(2,3);

functionfoo(n,m){

console.log(n*m);

}

FunctionexpressionsNamedfunctionscanalsobedefinedasanexpressionbydefiningananonymousfunctionandassigningittoavariable.

varbar=function(n,m){

console.log(n*m);

};

Theyarenothoistedlikefunctiondeclarationsare.Thisisbecause,whilefunctiondeclarationsarehoisted,variabledeclarationsarenot.Forexample,thiswillnotworkandwillthrowanerror:

bar(2,3);

varbar=function(n,m){

console.log(n*m);

};

Infunctionalprogramming,we’regoingtowanttousefunctionexpressionssowecantreatthefunctionslikevariables,makingthemavailabletobeusedascallbacksandargumentstohigher-orderfunctionssuchasmap()functions.Definingfunctionsasexpressionsmakesitmoreobviousthatthey’revariablesassignedtoafunction.Also,ifwe’regoingtowritefunctionsinonestyle,weshouldwriteallfunctionsinthatstyleforthesakeofconsistencyandclarity.

ThefunctionconstructorJavaScriptactuallyhasathirdwaytocreatefunctions:withtheFunction()constructor.Justlikefunctionexpressions,functionsdefinedwiththeFunction()constructorarenothoisted.

varfunc=newFunction('n','m','returnn+m');

func(2,3);//returns5

ButtheFunction()constructorisnotonlyconfusing,itisalsohighlydangerous.Nosyntaxcorrectioncanhappen,nooptimizationispossible.It’sfareasier,safer,andlessconfusingtowritethesamefunctionasfollows:

varfunc=function(n,m){returnn+m};

func(2,3);//returns5

UnpredictablebehaviorSothedifferenceisthatfunctiondeclarationsarehoistedwhilefunctionexpressionsarenot.Thiscancauseunexpectedthingstohappen.Considerthefollowing:

functionfoo(){

return'hi';

}

console.log(foo());

functionfoo(){

return'hello';

}

What’sactuallyprintedtotheconsoleishello.Thisisduetothefactthattheseconddefinitionofthefoo()functionishoistedtothetop,makingitthedefinitionthatisactuallyusedbytheJavaScriptinterpreter.

Whileatfirstthismaynotseemlikeacriticaldifference,infunctionalprogrammingthiscancausemayhem.Considerthefollowingcodesnippet:

if(true){

functionfoo(){console.log('one')};

}

else{

functionfoo(){console.log('two')};

}

foo();

Whenthefoo()functioniscalled,twoisprintedtotheconsole,notone!

Finally,thereisawaytocombinebothfunctionexpressionsanddeclarations.Itworksasfollows:

varfoo=functionbar(){console.log('hi');};

foo();//'hi'

bar();//Error:barisnotdefined

Itmakesverylittlesensetousethismethodbecausethenameusedinthedeclaration(thebar()functionintheprecedingexample)isnotavailableoutsidethefunctionandcausesconfusion.Itwouldonlybeappropriateforrecursion,forexample:

varfoo=functionfactorial(n){

if(n==0){

return1;

}

else{

returnn*factorial(n-1);

}

};

foo(5);

SummaryJavaScripthasbeencalledthe“assemblylanguageoftheweb,”becauseit’sasubiquitousandunavoidableasx86assembly.It’stheonlylanguagethatrunsonallbrowsers.It’salsoflawed,yetreferringtoitasalow-levellanguageismissingthemark.

Instead,thinkofJavaScriptastherawcoffeebeansoftheweb.Sure,someofthebeansaredamagedandafewarerotten.Butifthegoodonesareselected,roasted,andbrewedbyaskilledbarista,thebeanscanbetransformedintoabrilliantjamochathatcannotbehadjustonceandforgotten.It’sconsumptionbecomesadailycustom,lifewithoutitwouldbestatic,hardertoperform,andmuchlessexciting.Someevenprefertoenhancethebrewwithplug-insandadd-onssuchascream,sugar,andcocoa,whichcomplementitverywell.

OneofJavaScript’sbiggestcritics,DouglasCrawford,wasquotedassaying“TherearecertainlyalotofpeoplewhorefusetoconsiderthepossibilitythatJavaScriptgotanythingright.Iusedtobeoneofthoseguys.ButnowIcontinuetobeamazedbythebrilliancethatisinthere”.

JavaScriptturnedouttobeprettyawesome.

Chapter7.FunctionalandObject-orientedProgramminginJavaScriptYouwilloftenhearthatJavaScriptisablanklanguage,whereblankiseitherobject-oriented,functional,orgeneral-purpose.ThisbookhasfocusedonJavaScriptasafunctionallanguageandhasgonetogreatlengthstoprovethatitis.ButthetruthisthatJavaScriptisageneral-purposelanguage,meaningit’sfullycapableofmultipleprogrammingstyles.LikePythonandF#,JavaScriptismulti-paradigm.Butunlikethoselanguages,JavaScript’sOOPsideisprototype-basedwhilemostothergeneral-purposelanguagesareclass-based.

Inthisfinalchapter,wewillrelatebothfunctionalandobject-orientedprogrammingtoJavaScript,andseehowthetwoparadigmscancomplementeachotherandcoexistside-by-side.Inthischapterthefollowingtopicswillbecovered:

HowcanJavaScriptbebothfunctionalandOOP?JavaScript’sOOP–usingprototypesHowtomixfunctionalandOOPinJavaScriptFunctionalinheritanceFunctionalmixins

Bettercodeisthegoal.Functionalandobject-orientedprogrammingarejustmeanstothisend.

JavaScript–themulti-paradigmlanguageIfobject-orientedprogrammingmeanstreatingallvariablesasobjects,andfunctionalprogrammingmeanstreatingallfunctionsasvariables,thencan’tfunctionsbetreatedlikeobjects?InJavaScript,theycan.

Butsayingthatfunctionalprogrammingmeanstreatingfunctionsasvariablesissomewhatinaccurate.Abetterwaytoputitis:functionalprogrammingmeanstreatingeverythingasavalue,especiallyfunctions.

Abetterwaystilltodescribefunctionalprogrammingmaybetocallitdeclarative.Independentoftheimperativebranchofprogrammingstyles,declarativeprogrammingexpressesthelogicofcomputationrequiredtosolvetheproblem.Thecomputeristoldwhattheproblemisratherthantheprocedureforhowtosolveit.

Meanwhile,object-orientedprogrammingisderivedfromtheimperativeprogrammingstyle:thecomputerisgivenstep-by-stepinstructionsforhowtosolvetheproblem.OOPmandatesthattheinstructionsforcomputation(methods)andthedatatheyworkon(membervariables)beorganizedintounitscalledobjects.Theonlywaytoaccessthatdataisthroughtheobject’smethods.

Sohowcanthesetwostylesbeintegratedtogether?

Thecodeinsidetheobject’smethodsistypicallywritteninanimperativestyle.Butwhatifitwasinafunctionalstyle?Afterall,OOPdoesn’texcludeimmutabledataandhigher-orderfunctions.Perhapsapurerwaytomixthetwowouldbetotreatobjectsbothasfunctionsandastraditional,class-basedobjectsatthesametime.Maybewecansimplyincludeseveralideasfromfunctionalprogramming—suchaspromisesandrecursion—intoourobject-orientedapplication.OOPcoverstopicssuchasencapsulation,polymorphism,andabstraction.Sodoesfunctionalprogramming,itjustgoesaboutitinadifferentway.Somaybewecanincludeseveralideasfromobject-orientedprogramminginourfunctional-orientedapplication.

Thepointis:OOPandFPcanbemixedtogetherandthereareseveralwaystodoit.They’renotexclusiveofeachother.

JavaScript’sobject-orientedimplementation–usingprototypesJavaScriptisaclass-lesslanguage.That’snottomeanitislessfashionableormoreblue-collarthanothercomputerlanguages;class-lessmeansitdoesn’thaveaclassstructureinthesamewaythatobject-orientedlanguagesdo.Instead,itusesprototypesforinheritance.

AlthoughthismaybebafflingtoprogrammerswithbackgroundsinC++andJava,prototype-basedinheritancecanbemuchmoreexpressivethantraditionalinheritance.ThefollowingisabriefcomparisonbetweenthedifferencesbetweenC++andJavaScript:

C++ JavaScript

Stronglytyped Looselytyped

Static Dynamic

Class-based Prototype-based

Classes Functions

Constructors Functions

Methods Functions

InheritanceBeforewegomuchfurther,let’smakesurewefullyunderstandtheconceptofinheritanceinobject-orientedprogramming.Class-basedinheritanceisdemonstratedinthefollowingpseudo-code:

classPolygon{

intnumSides;

functioninit(n){

numSides=n;

}

}

classRectangleinheritsPolygon{

intwidth;

intlength;

functioninit(w,l){

numSides=4;

width=w;

length=l;

}

functiongetArea(){

returnw*l;

}

}

classSquareinheritsRectangle{

functioninit(s){

numSides=4;

width=s;

length=s;

}

}

ThePolygonclassistheparentclasstheotherclassesinheritfrom.Itdefinesjustonemembervariable,thenumberofsides,whichissetintheinit()function.TheRectanglesubclassinheritsfromthePolygonclassandaddstwomoremembervariables,lengthandwidth,andamethod,getArea().Itdoesn’tneedtodefinethenumSidesvariablebecauseitwasalreadydefinedbytheclassitinheritsfrom,anditalsooverridestheinit()function.TheSquareclasscarriesonthischainofinheritanceevenfurtherbyinheritingfromtheRectangleclassforitsgetArea()method.Bysimplyoverridingtheinit()functionagainsuchthatthelengthandwidtharethesame,thegetArea()functioncanremainunchangedandlesscodeneedstobewritten.

InatraditionalOOPlanguage,thisiswhatinheritanceisallabout.Ifwewantedtoaddacolorpropertytoalltheobjects,allwewouldhavetodoisaddittothePolygonobjectwithouthavingtomodifyanyoftheobjectsthatinheritfromit.

JavaScript’sprototypechainInheritanceinJavaScriptcomesdowntoprototypes.Eachobjecthasaninternalpropertyknownasitsprototype,whichisalinktoanotherobject.Thatobjecthasaprototypeofitsown.Thispatterncanrepeatuntilanobjectisreachedthathasundefinedasitsprototype.Thisisknownastheprototypechain,andit’showinheritanceworksinJavaScript.ThefollowingdiagramexplaintheinheritanceinJavaScirpt:

Whenrunningasearchforanobject’sfunctiondefinition,JavaScript“walks”theprototypechainuntilitfindsthefirstdefinitionofafunctionwiththerightname.Therefore,overridingitisassimpleasprovidinganewdefinitionontheprototypeofthesubclass.

InheritanceinJavaScriptandtheObject.create()methodJustastherearemanywaystocreateobjectsinJavaScript,therearealsomanywaystoreplicateclass-based,classicalinheritance.ButtheonepreferredwaytodoitiswiththeObject.create()method.

varPolygon=function(n){

this.numSides=n;

}

varRectangle=function(w,l){

this.width=w;

this.length=l;

}

//theRectangle'sprototypeisredefinedwithObject.create

Rectangle.prototype=Object.create(Polygon.prototype);

//it'simportanttonowrestoretheconstructorattribute

//otherwiseitstayslinkedtothePolygon

Rectangle.prototype.constructor=Rectangle;

//nowwecancontinuetodefinetheRectangleclass

Rectangle.prototype.numSides=4;

Rectangle.prototype.getArea=function(){

returnthis.width*this.length;

}

varSquare=function(w){

this.width=w;

this.length=w;

}

Square.prototype=Object.create(Rectangle.prototype);

Square.prototype.constructor=Square;

vars=newSquare(5);

console.log(s.getArea());//25

Thissyntaxmayseemunusualtomanybut,withalittlepractice,itwillbecomefamiliar.Theprototypekeywordmustbeusedtogainaccesstotheinternalproperty,[[Prototype]],whichallobjectshave.TheObject.create()methoddeclaresanewobjectwithaspecifiedobjectforitsprototypetoinheritfrom.Inthisway,classicalinheritancecanbeachievedinJavaScript.

NoteTheObject.create()methodwasintroducedinECMAScript5.1in2011,anditwasbilledasthenewandpreferredwaytocreateobjects.ThiswasjustoneofmanyattemptstointegrateinheritanceintoJavaScript.Thankfully,thismethodworksprettywell.

WesawthisstructureofinheritancewhenbuildingtheMaybeclassesinChapter5,

CategoryTheory.HerearetheMaybe,None,andJustclasses,whichinheritfromeachotherjustliketheprecedingexample.

varMaybe=function(){};

varNone=function(){};

None.prototype=Object.create(Maybe.prototype);

None.prototype.constructor=None;

None.prototype.toString=function(){return'None';};

varJust=function(x){this.x=x;};

Just.prototype=Object.create(Maybe.prototype);

Just.prototype.constructor=Just;

Just.prototype.toString=function(){return"Just"+this.x;};

ThisshowsthatclassinheritanceinJavaScriptcanbeanenableroffunctionalprogramming.

AcommonmistakeistopassaconstructorintoObject.create()insteadofaprototypeobject.Thisproblemiscompoundedbythefactthatanerrorwillnotbethrownuntilthesubclasstriestouseaninheritedmemberfunction.

Foo.prototype=Object.create(Parent.prototype);//correct

Bar.prototype=Object.create(Parent);//incorrect

Bar.inheritedMethod();//Error:functionisundefined

Thefunctionwon’tbefoundiftheinheritedMethod()methodhasbeenattachedtotheFoo.prototypeclass.IftheinheritedMethod()methodisattacheddirectlytotheinstancewiththis.inheritedMethod=function(){...}intheBarconstructor,thenthisuseofParentasanargumentofObject.create()couldbecorrect.

Mixingfunctionalandobject-orientedprogramminginJavaScriptObject-orientedprogramminghasbeenthedominantprogrammingparadigmforseveraldecades.ItistaughtinComputerScience101classesaroundtheworld,whilefunctionalprogrammingisnot.Itiswhatsoftwarearchitectsusetodesignapplications,whilefunctionalprogrammingisnot.Anditmakessensetoo:OOPmakesiteasytoconceptualizeabstractideas.Itmakesiteasiertowritecode.

So,unlessyoucanconvinceyourbossthattheapplicationneedstobeallfunctional,we’regoingtobeusingfunctionalprogramminginanobject-orientedworld.Thissectionwillexplorewaystodothis.

FunctionalinheritancePerhapsthemostaccessiblewaytoapplyfunctionalprogrammingtoJavaScriptapplicationsistouseamostlyfunctionalstylewithinOOPprinciples,suchasinheritance.

Toexplorehowthismightwork,let’sbuildasimpleapplicationthatcalculatesthepriceofaproduct.First,we’llneedsomeproductclasses:

varShirt=function(size){

this.size=size;

};

varTShirt=function(size){

this.size=size;

};

TShirt.prototype=Object.create(Shirt.prototype);

TShirt.prototype.constructor=TShirt;

TShirt.prototype.getPrice=function(){

if(this.size=='small'){

return5;

}

else{

return10;

}

}

varExpensiveShirt=function(size){

this.size=size;

}

ExpensiveShirt.prototype=Object.create(Shirt.prototype);

ExpensiveShirt.prototype.constructor=ExpensiveShirt;

ExpensiveShirt.prototype.getPrice=function(){

if(this.size=='small'){

return20;

}

else{

return30;

}

}

WecanthenorganizethemwithinaStoreclassasfollows:

varStore=function(products){

this.products=products;

}

Store.prototype.calculateTotal=function(){

returnthis.products.reduce(function(sum,product){

returnsum+product.getPrice();

},10)*TAX;//startwith$10markup,timesglobalTAXvar

};

varTAX=1.08;

varp1=newTShirt('small');

varp2=newExpensiveShirt('large');

vars=newStore([p1,p2]);

console.log(s.calculateTotal());//Output:35

ThecalculateTotal()methodusesthearray’sreduce()functiontocleanlysumtogetherthepricesoftheproducts.

Thisworksjustfine,butwhatifweneedadynamicwaytocalculatethemarkupvalue?Forthis,wecanturntoaconceptcalledStrategyPattern.

StrategyPatternStrategyPatternisamethodfordefiningafamilyofinterchangeablealgorithms.ItisusedbyOOPprogrammerstomanipulatebehavioratruntime,butitisbasedonafewfunctionalprogrammingprinciples:

SeparationoflogicanddataCompositionoffunctionsFunctionsasfirst-classobjects

AndacoupleofOOPprinciplesaswell:

EncapsulationInheritance

Inourexampleapplicationforcalculatingproductcost,explainedpreviously,let’ssaywewanttogivepreferentialtreatmenttocertaincustomers,andthatthemarkupwillhavetobeadjustedtoreflectthis.

Solet’screatesomecustomerclasses:

varCustomer=function(){};

Customer.prototype.calculateTotal=function(products){

returnproducts.reduce(function(total,product){

returntotal+product.getPrice();

},10)*TAX;

};

varRepeatCustomer=function(){};

RepeatCustomer.prototype=Object.create(Customer.prototype);

RepeatCustomer.prototype.constructor=RepeatCustomer;

RepeatCustomer.prototype.calculateTotal=function(products){

returnproducts.reduce(function(total,product){

returntotal+product.getPrice();

},5)*TAX;

};

varTaxExemptCustomer=function(){};

TaxExemptCustomer.prototype=Object.create(Customer.prototype);

TaxExemptCustomer.prototype.constructor=TaxExemptCustomer;

TaxExemptCustomer.prototype.calculateTotal=function(products){

returnproducts.reduce(function(total,product){

returntotal+product.getPrice();

},10);

};

EachCustomerclassencapsulatesthealgorithm.NowwejustneedtheStoreclasstocall

theCustomerclass’scalculateTotal()method.

varStore=function(products){

this.products=products;

this.customer=newCustomer();

//bonusexercise:useMaybesfromChapter5insteadofadefault

customerinstance

}

Store.prototype.setCustomer=function(customer){

this.customer=customer;

}

Store.prototype.getTotal=function(){

returnthis.customer.calculateTotal(this.products);

};

varp1=newTShirt('small');

varp2=newExpensiveShirt('large');

vars=newStore([p1,p2]);

varc=newTaxExemptCustomer();

s.setCustomer(c);

s.getTotal();//Output:45

TheCustomerclassesdothecalculating,theProductclassesholdthedata(theprices),andtheStoreclassmaintainsthecontext.Thisachievesaveryhighlevelofcohesionandaverygoodmixtureofobject-orientedprogrammingandfunctionalprogramming.JavaScript’shighlevelofexpressivenessmakesthispossibleandquiteeasy.

MixinsInanutshell,mixinsareclassesthatcanallowotherclassestousetheirmethods.Themethodsareintendedtobeusedsolelybyotherclasses,andthemixinclassitselfisnevertobeinstantiated.Thishelpstoavoidinheritanceambiguity.Andthey’reagreatmeansofmixingfunctionalprogrammingwithobject-orientedprogramming.

Mixinsareimplementeddifferentlyineachlanguage.ThankstoJavaScript’sflexibilityandexpressiveness,mixinsareimplementedasobjectswithonlymethods.Whiletheycanbedefinedasfunctionobjects(thatis,varmixin=function(){...};),itwouldbebetterforthestructuraldisciplineofthecodetodefinethemasobjectliterals(thatis,varmixin={...};).Thiswillhelpustodistinguishbetweenclassesandmixins.Afterall,mixinsshouldbetreatedasprocesses,notobjects.

Let’sstartwithdeclaringsomemixins.We’llextendourStoreapplicationfromtheprevioussection,usingmixinstoexpandontheclasses.

varsmall={

getPrice:function(){

returnthis.basePrice+6;

},

getDimensions:function(){

return[44,63]

}

}

varlarge={

getPrice:function(){

returnthis.basePrice+10;

},

getDimensions:function(){

return[64,83]

}

};

We’renotlimitedtojustthis.Manymoremixinscanbeadded,likecolorsorfabricmaterial.We’llhavetorewriteourShirtclassesalittlebit,asshowninthefollowingcodesnippet:

varShirt=function(){

this.basePrice=1;

};

Shirt.getPrice=function(){

returnthis.basePrice;

}

varTShirt=function(){

this.basePrice=5;

};

TShirt.prototype=Object.create(Shirt.prototype);

TShirt..prototype.constructor=TShirt;

Nowwe’rereadytousemixins.

Classicalmixins

You’reprobablywonderingjusthowthesemixinsgetmixedwiththeclasses.Theclassicalwaytodothisisbycopyingthemixin’sfunctionsintothereceivingobject.ThiscanbedonewiththefollowingextensiontotheShirtprototype:

Shirt.prototype.addMixin=function(mixin){

for(varpropinmixin){

if(mixin.hasOwnProperty(prop)){

this.prototype[prop]=mixin[prop];

}

}

};

Andnowthemixinscanbeaddedasfollows:

TShirt.addMixin(small);

varp1=newTShirt();

console.log(p1.getPrice());//Output:11

TShirt.addMixin(large);

varp2=newTShirt();

console.log(p2.getPrice());//Output:15

However,thereisamajorproblem.Whenthepriceofp1iscalculatedagain,itcomesbackas15,thepriceofalargeitem.Itshouldbethevalueforasmallone!

console.log(p1.getPrice());//Output:15

TheproblemisthattheShirtobject’sprototype.getPrice()methodisgettingrewritteneverytimeamixinisaddedtoit;thisisnotveryfunctionalatallandnotwhatwewant.

FunctionalmixinsThere’sanotherwaytousemixins,onethatismorealignedwithfunctionalprogramming.

Insteadofcopyingthemethodsofthemixintothetargetobject,weneedtocreateanewobjectthatisacloneofthetargetobjectwiththemixin’smethodsaddedin.Theobjectmustbeclonedfirst,andthisisachievedbycreatinganewobjectthatinheritsfromit.We’llcallthisvariationplusMixin.

Shirt.prototype.plusMixin=function(mixin){

//createanewobjectthatinheritsfromtheold

varnewObj=this;

newObj.prototype=Object.create(this.prototype);

for(varpropinmixin){

if(mixin.hasOwnProperty(prop)){

newObj.prototype[prop]=mixin[prop];

}

}

returnnewObj;

};

varSmallTShirt=Tshirt.plusMixin(small);//createsanewclass

varsmallT=newSmallTShirt();

console.log(smallT.getPrice());//Output:11

varLargeTShirt=Tshirt.plusMixin(large);

varlargeT=newLargeTShirt();

console.log(largeT.getPrice());//Output:15

console.log(smallT.getPrice());//Output:11(noteffectedby2ndmixin

call)

Herecomesthefunpart!Nowwecangetreallyfunctionalwiththemixins.Wecancreateeverypossiblecombinationofproductsandmixins.

//intherealworldtherewouldbewaymoreproductsandmixins!

varproductClasses=[ExpensiveShirt,Tshirt];

varmixins=[small,medium,large];

//mixthemalltogether

products=productClasses.reduce(function(previous,current){

varnewProduct=mixins.map(function(mxn){

varmixedClass=current.plusMixin(mxn);

vartemp=newmixedClass();

returntemp;

});

returnprevious.concat(newProduct);

},[]);

products.forEach(function(o){console.log(o.getPrice())});

Tomakeitmoreobject-oriented,wecanrewritetheStoreobjectwiththisfunctionality.We’llalsoaddadisplayfunctiontotheStoreobject,nottheproducts,tokeeptheinterfacelogicandthedataseparated.

//thestore

varStore=function(){

productClasses=[ExpensiveShirt,TShirt];

productMixins=[small,medium,large];

this.products=productClasses.reduce(function(previous,current){

varnewObjs=productMixins.map(function(mxn){

varmixedClass=current.plusMixin(mxn);

vartemp=newmixedClass();

returntemp;

});

returnprevious.concat(newObjs);

},[]);

}

Store.prototype.displayProducts=function(){

this.products.forEach(function(p){

$('ul#products').append('<li>'+p.getTitle()+':

$'+p.getPrice()+'</li>');

});

}

AndallwehavetodoiscreateaStoreobjectandcallitsdisplayProducts()methodtogeneratealistofproductsandprices!

<ulid="products">

<li>smallpremiumshirt:$16</li>

<li>mediumpremiumshirt:$18</li>

<li>largepremiumshirt:$20</li>

<li>smallt-shirt:$11</li>

<li>mediumt-shirt:$13</li>

<li>larget-shirt:$15</li>

</ul>

Theselinesneedtobeaddedtotheproductclassesandmixinstogettheprecedingoutputtowork:

Shirt.prototype.title='shirt';

TShirt.prototype.title='t-shirt';

ExpensiveShirt.prototype.title='premiumshirt';

//thenthemixinsgottheextra'getTitle'function:

varsmall={

...

getTitle:function(){

return'small'+this.title;//smallormediumorlarge

}

}

And,justlikethat,wehaveane-commerceapplicationthatishighlymodularandextendable.Newshirtstylescanbeaddedabsurdlyeasily—justdefineanewShirtsubclassandaddtoittheStoreclass’sarrayproductclasses.Mixinsareaddedinjustthesameway.Sonowwhenourbosssays,“Hey,wehaveanewtypeofshirtandacoat,eachavailableinthestandardcolors,andweneedthemaddedtothewebsitebeforeyougohometoday”,wecanrestassuredthatwe’llnotbestayinglate!

SummaryJavaScripthasahighlevelofexpressiveness.Thismakesitpossibletomixfunctionalandobject-orientedprogramming.ModernJavaScriptisnotsolelyOOPorfunctional—itisamixtureofthetwo.ConceptssuchasStrategyPatternandmixinsareperfectforJavaScript’sprototypestructure,andtheyhelptoprovethattoday’sbestpracticesinJavaScriptshareequalamountsoffunctionalprogrammingandobject-orientedprogramming.

Ifyouweretotakeawayonlyonethingfromthisbook,Iwouldwantittobehowtoapplyfunctionalprogrammingtechniquestoreal-worldapplications.Andthischaptershowedyouhowtodoexactlythat.

AppendixA.CommonFunctionsforFunctionalProgramminginJavaScriptThisAppendixcoverscommonfunctionsforfunctionalprogramminginJavaScript:

ArrayFunctions:

varflatten=function(arrays){

returnarrays.reduce(function(p,n){

returnp.concat(n);

});

};

varinvert=function(arr){

returnarr.map(function(x,i,a){

returna[a.length-(i+1)];

});

};

BindingFunctions:

varbind=Function.prototype.call.bind(Function.prototype.bind);

varcall=bind(Function.prototype.call,Function.prototype.call);

varapply=bind(Function.prototype.call,Function.prototype.apply);

CategoryTheory:

varcheckTypes=function(typeSafeties){

arrayOf(func)(arr(typeSafeties));

varargLength=typeSafeties.length;

returnfunction(args){

arr(args);

if(args.length!=argLength){

thrownewTypeError('Expected'+argLength+'arguments');

}

varresults=[];

for(vari=0;i<argLength;i++){

results[i]=typeSafeties[i](args[i]);

}

returnresults;

};

};

varhomoMorph=function(/*arg1,arg2,...,argN,output*/){

varbefore=checkTypes(arrayOf(func)

(Array.prototype.slice.call(arguments,0,arguments.length-1)));

varafter=func(arguments[arguments.length-1])

returnfunction(middle){

returnfunction(args){

returnafter(middle.apply(this,

before([].slice.apply(arguments))));

};

};

};

Composition:

Function.prototype.compose=function(prevFunc){

varnextFunc=this;

returnfunction(){

returnnextFunc.call(this,prevFunc.apply(this,arguments));

};

};

Function.prototype.sequence=function(prevFunc){

varnextFunc=this;

returnfunction(){

returnprevFunc.call(this,nextFunc.apply(this,arguments));

};

};

Currying:

Function.prototype.curry=function(numArgs){

varfunc=this;

numArgs=numArgs||func.length;

//recursivelyacquirethearguments

functionsubCurry(prev){

returnfunction(arg){

varargs=prev.concat(arg);

if(args.length<numArgs){

//recursivecase:westillneedmoreargs

returnsubCurry(args);

}

else{

//basecase:applythefunction

returnfunc.apply(this,args);

}

};

};

returnsubCurry([]);

};

Functors:

//map::(a->b)->[a]->[b]

varmap=function(f,a){

returnarr(a).map(func(f));

}

//strmap::(str->str)->str->str

varstrmap=function(f,s){

returnstr(s).split('').map(func(f)).join('');

}

//fcompose::(a->b)*->(a->b)

varfcompose=function(){

varfuncs=arrayOf(func)(arguments);

returnfunction(){

varargsOfFuncs=arguments;

for(vari=funcs.length;i>0;i-=1){

argsOfFuncs=[funcs[i].apply(this,args)];

}

returnargs[0];

};

};

Lenses:

varlens=function(get,set){

varf=function(a){returnget(a)};

f.get=function(a){returnget(a)};

f.set=set;

f.mod=function(f,a){returnset(a,f(get(a)))};

returnf;

};

//usage:

varfirst=lens(

function(a){returnarr(a)[0];},//get

function(a,b){return[b].concat(arr(a).slice(1));}//set

);

Maybes:

varMaybe=function(){};

Maybe.prototype.orElse=function(y){

if(thisinstanceofJust){

returnthis.x;

}

else{

returny;

}

};

varNone=function(){};

None.prototype=Object.create(Maybe.prototype);

None.prototype.toString=function(){return'None';};

varnone=function(){returnnewNone()};

//andtheJustinstance,awrapperforanobjectwithavalue

varJust=function(x){returnthis.x=x;};

Just.prototype=Object.create(Maybe.prototype);

Just.prototype.toString=function(){return"Just"+this.x;};

varjust=function(x){returnnewJust(x)};

varmaybe=function(m){

if(minstanceofNone){

returnm;

}

elseif(minstanceofJust){

returnjust(m.x);

}

else{

thrownewTypeError("Error:JustorNoneexpected,"+m.toString()

+"given.");

}

};

varmaybeOf=function(f){

returnfunction(m){

if(minstanceofNone){

returnm;

}

elseif(minstanceofJust){

returnjust(f(m.x));

}

else{

thrownewTypeError("Error:JustorNoneexpected,"+

m.toString()+"given.");

}

};

};

Mixins:

Object.prototype.plusMixin=function(mixin){

varnewObj=this;

newObj.prototype=Object.create(this.prototype);

newObj.prototype.constructor=newObj;

for(varpropinmixin){

if(mixin.hasOwnProperty(prop)){

newObj.prototype[prop]=mixin[prop];

}

}

returnnewObj;

};

PartialApplication:

functionbindFirstArg(func,a){

returnfunction(b){

returnfunc(a,b);

};

};

Function.prototype.partialApply=function(){

varfunc=this;

args=Array.prototype.slice.call(arguments);

returnfunction(){

returnfunc.apply(this,args.concat(

Array.prototype.slice.call(arguments)

));

};

};

Function.prototype.partialApplyRight=function(){

varfunc=this;

args=Array.prototype.slice.call(arguments);

returnfunction(){

returnfunc.apply(

this,

Array.protype.slice.call(arguments,0)

.concat(args));

};

};

Trampolining:

vartrampoline=function(f){

while(f&&finstanceofFunction){

f=f.apply(f.context,f.args);

}

returnf;

};

varthunk=function(fn){

returnfunction(){

varargs=Array.prototype.slice.apply(arguments);

returnfunction(){returnfn.apply(this,args);};

};

};

TypeSafeties:

vartypeOf=function(type){

returnfunction(x){

if(typeofx===type){

returnx;

}

else{

thrownewTypeError("Error:"+type+"expected,"+typeofx+"

given.");

}

};

};

varstr=typeOf('string'),

num=typeOf('number'),

func=typeOf('function'),

bool=typeOf('boolean');

varobjectTypeOf=function(name){

returnfunction(o){

if(Object.prototype.toString.call(o)==="[object"+name+"]"){

returno;

}

else{

thrownewTypeError("Error:'+name+'expected,somethingelse

given.");

}

};

};

varobj=objectTypeOf('Object');

vararr=objectTypeOf('Array');

vardate=objectTypeOf('Date');

vardiv=objectTypeOf('HTMLDivElement');

//arrayOf::(a->b)->([a]->[b])

vararrayOf=function(f){

returnfunction(a){

returnmap(func(f),arr(a));

}

};

Y-combinator:

varY=function(F){

return(function(f){

returnf(f);

}(function(f){

returnF(function(x){

returnf(f)(x);

});

}));

};

//MemoizingY-Combinator:

varYmem=function(F,cache){

if(!cache){

cache={};//Createanewcache.

}

returnfunction(arg){

if(cache[arg]){

//Answerincache

returncache[arg];

}

//elsecomputetheanswer

varanswer=(F(function(n){

return(Ymem(F,cache))(n);

}))(arg);//Computetheanswer.

cache[arg]=answer;//Cachetheanswer.

returnanswer;

};

};

AppendixB.GlossaryofTermsThisappendixcoverssomeoftheimportanttermsthatareusedinthisbook:

Anonymousfunction:Afunctionthathasnonameandisnotboundtoanyvariables.ItisalsoknownasaLambdaExpression.Callback:Afunctionthatcanbepassedtoanotherfunctiontobeusedinalaterevent.Category:IntermsofCategoryTheory,acategoryisacollectionofobjectsofthesametype.InJavaScript,acategorycanbeanarrayorobjectthatcontainsobjectsthatareallexplicitlydeclaredasnumbers,strings,Booleans,dates,objects,andsoon.CategoryTheory:Aconceptthatorganizesmathematicalstructuresintocollectionsofobjectsandoperationsonthoseobjects.Thedatatypesandfunctionsusedincomputerprogramsformthecategoriesusedinthisbook.Closure:Anenvironmentsuchthatfunctionsdefinedwithinitcanaccesslocalvariablesthatarenotavailableoutsideit.Coupling:Thedegreetowhicheachprogrammodulereliesoneachoftheothermodules.Functionalprogrammingreducestheamountofcouplingwithinaprogram.Currying:Theprocessoftransformingafunctionwithmanyargumentsintoafunctionwithoneargumentthatreturnsanotherfunctionthatcantakemorearguments,asneeded.Formally,afunctionwithNargumentscanbetransformedintoafunctionchainofNfunctions,eachwithonlyoneargument.Declarativeprogramming:Aprogrammingstylethatexpressesthecomputationallogicrequiredtosolvetheproblem.Thecomputeristoldwhattheproblemisratherthantheprocedurerequiredtosolveit.Endofunctor:Afunctorthatmapsacategorytoitself.Functioncomposition:Theprocessofcombiningmanyfunctionsintoonefunction.Theresultofeachfunctionispassedasanargumenttothenext,andtheresultofthelastfunctionistheresultofthewholecomposition.Functionallanguage:Acomputerlanguagethatfacilitatesfunctionalprogramming.Functionalprogramming:Adeclarativeprogrammingparadigmthatfocusesontreatingfunctionsasmathematicalexpressionsandavoidsmutabledataandchangesinstate.Functionalreactiveprogramming:Astyleoffunctionalprogrammingthatfocusesonreactiveelementsandvariablesthatchangeovertimeinresponsetoevents.Functor:Amappingbetweencategories.Higher-orderfunction:Afunctionthattakeseitheroneormorefunctionsasinput,andreturnsafunctionasitsoutput.Inheritance:Anobject-orientedprogrammingcapabilitythatallowsoneclasstoinheritmembervariablesandmethodsfromanotherclass.Lambdaexpressions:SeeAnonymousfunction.Lazyevaluation:Acomputerlanguageevaluationstrategythatdelaystheevaluationofanexpressionuntilitsvalueisneeded.Theoppositeofthisstrategyiscalledeager

evaluationorgreedyevaluation.Lazyevaluationisalsoknownascallbyneed.Library:Asetofobjectsandfunctionsthathaveawell-definedinterfacethatallowsathird-partyprogramtoinvoketheirbehavior.Memoization:Thetechniqueofstoringtheresultsofexpensivefunctioncalls.Whenthefunctioniscalledlaterwiththesamearguments,thestoredresultisreturnedratherthancomputingtheresultagain.Methodchain:Apatterninwhichmanymethodsareinvokedsidebysidebydirectlypassingtheoutputofonemethodtotheinputofthenext.Thisavoidstheneedtoassigntheintermediaryvaluestotemporaryvariables.Mixin:Anobjectthatcanallowotherobjectstouseitsmethods.Themethodsareintendedtobeusedsolelybyotherobjects,andthemixinobjectitselfisnevertobeinstantiated.Modularity:Thedegreetowhichaprogramcanbebrokendownintoindependentmodulesofcode.Functionalprogrammingincreasesthemodularityofprograms.Monad:Astructurethatprovidestheencapsulationrequiredbyfunctors.Morphism:Apurefunctionthatonlyworksonacertaincategoryandalwaysreturnsthesameoutputwhengivenaspecificsetofinputs.Homomorphicoperationsarerestrictedtoasinglecategory,whilepolymorphicoperationscanoperateonmultiplecategories.Partialapplication:Theprocessofbindingvaluestooneormoreargumentsofafunction.Itreturnsapartiallyappliedfunction,whichinturnacceptstheremaining,unboundarguments.Polyfill:Afunctionusedtoaugmentprototypeswithnewfunctions.Itallowsustocallournewfunctionsasmethodsofthepreviousfunction.Purefunction:Afunctionwhoseoutputvaluedependsonlyontheargumentsthataretheinputtothefunction.Thus,callingafunction,f,twicewiththesamevalueofanargument,x,willproducethesameresult,f(x),everytime.Recursivefunction:Afunctionthatcallsitself.Suchfunctionsdependonsolutionstosmallerinstancesofthesameproblemtocomputethesolutiontothelargerproblem.Likeiteration,recursionisanotherwaytorepeatedlycallthesameblockofcode.But,unlikeiteration,recursionrequiresthatthecodeblockdefinethecaseinwhichtherepeatingcodecallsshouldterminate,knownasthebasecase.Reusability:Thedegreetowhichablockofcode,usuallyafunctioninJavaScript,canbereusedinotherpartsofthesameprogramorinotherprograms.Self-invokingfunction:Ananonymousfunctionthatisinvokedimmediatelyafterithasbeendefined.InJavaScript,thisisachievedbyplacingapairofparenthesesafterthefunctionexpression.Strategypattern:Amethodusedtodefineafamilyofinterchangeablealgorithms.Tailrecursion:Astack-basedimplementationofrecursion.Foreveryrecursivecall,thereisanewframeinthestack.Toolkit:Asmallsoftwarelibrarythatprovidesasetoffunctionsfortheprogrammertouse.Comparedtoalibrary,atoolkitissimplerandrequireslesscouplingwiththeprogramthatinvokesit.Trampolining:Astrategyforrecursionthatprovidestail-calleliminationin

programminglanguagesthatdonotprovidethisfeature,suchasJavaScript.Y-combinator:Afixed-pointcombinatorinLambdacalculusthateliminatesexplicitrecursion.Whenitisgivenasinputtoafunctionthatreturnsarecursivefunction,theY-combinatorreturnsthefixedpointofthatfunction,whichisthetransformationfromtherecursivefunctiontoanon-recursivefunction.

IndexA

anonymousfunctions/Anonymousfunctionsapply()function/Apply,call,andthethiskeywordarrayOffunctor/Arraysandfunctorsarrays

about/Arraysandfunctors

Bbackbone.js

about/IntroductionBacon.js/Bacon.jsBilby.js/Bilby.jsbind()function/Bindingargumentsbluebird/Promises

CC++

versusJavaScript/JavaScript’sobject-orientedimplementation–usingprototypes

call()function/Apply,call,andthethiskeywordcategories

about/Categorytheoryinanutshellimplementing/Implementingcategories

categorytheoryabout/Categorytheory,Categorytheoryinanutshelltypesafetyfunctions,creating/Typesafetyobjects/Objectidentities

classicalmixinsabout/Classicalmixins

Clojureabout/Introduction

closureabout/Self-invokingfunctionsandclosuresusing/Self-invokingfunctionsandclosures

CommandLineInterface(CLI)/CLIcompose

programmingwith/Programmingwithcomposecurrying

about/Partialfunctionapplicationandcurrying,Currying

Ddevelopmentenvironment

about/DevelopmentandproductionenvironmentsDojo

about/Introduction

Ee-commercewebsiteapplication

about/Theapplication–ane-commercewebsiteimperativemethods/Imperativemethods

ease.jsabout/IsJavaScriptafunctionalprogramminglanguage?

ECMAScriptabout/IsJavaScriptafunctionalprogramminglanguage?

endofunctorsabout/jQueryisamonad

enginesabout/IsJavaScriptafunctionalprogramminglanguage?

environments,JavaScriptapplicationsbrowsers/Browsersserver-sideJavaScript/Server-sideJavaScriptCommandLineInterface(CLI)/CLI

FFantasyLand/FantasyLandfilter()function

parameters/Array.prototype.filter()forEach()function

parameters/Array.prototype.forEachfunctionalinheritance

about/FunctionalinheritanceStrategyPattern/Functionalinheritance,StrategyPattern

functionallanguagecompiling,intoJavaScript/FunctionallanguagesthatcompileintoJavaScript

Functionallibrariesusing/UsingfunctionallibrarieswithotherJavaScriptmodules

Functionallibraries,forJavaScriptabout/FunctionallibrariesforJavaScriptUnderscore.js/Underscore.jsFantasyLand/FantasyLandBilby.js/Bilby.jsLazy.js/Lazy.jsBacon.js/Bacon.jsFFunctional/Honorablementionswwu.js/Honorablementionssloth.js/Honorablementionsstream.js/HonorablementionsLo-Dash.js/HonorablementionsSsugar/Honorablementionsfrom.js/HonorablementionsJSLINQ/HonorablementionsBoiler.js/HonorablementionsFFolktale/HonorablementionsjjQuery/Honorablementions

functionalmixinsabout/Functionalmixins

functionalprogrammingabout/Introduction,Functionalprogramming,Mostlyfunctionalprogrammingused,innonfunctionalprogramming/Functionalprogramminginanonfunctionalworldevents,handling/Handlingeventsandobject-orientedprogramming,mixing/Mixingfunctionalandobject-orientedprogramminginJavaScript

functionalprogramming,usingobject-orientedprogrammingfunctionalinheritance/Functionalinheritancemixins/Mixins

functionalprogramminglanguageJavaScript/IsJavaScriptafunctionalprogramminglanguage?

functionalprogramminglanguagesabout/Functionalprogramminglanguagesperforming/Whatmakesalanguagefunctional?characteristics/Whatmakesalanguagefunctional?advantages/Advantages,Modularity,Mathematicallycorrect

functionalreactiveprogrammingabout/Functionalreactiveprogrammingreactivity/Reactivitysubscriber,modifying/Puttingitalltogether

FunctionalReactiveProgramming(FRP)/Functionalreactiveprogrammingfunctioncomposition

about/Functioncomposition,Functioncompositions,revisitedcompose()/Composesequence,using/Sequence–composeinreversecompositions,versuschains/Compositionsversuschainsrewriting/Functioncompositions,revisited

functionconstructorabout/Thefunctionconstructor

functiondeclarationsabout/Functiondeclarationsversusfunctionexpressions/Functionexpressions,Unpredictablebehavior

functionexpressionsabout/Functionexpressionsversusfunctionconstructor/Thefunctionconstructor

functionfactories/Functionfactoriesfunctionmanipulation

about/Functionmanipulationapply()function/Apply,call,andthethiskeywordthiskeyword/Apply,call,andthethiskeywordcall()function/Apply,call,andthethiskeywordbind()function/Bindingargumentsfunctionfactories/Functionfactories

Functionsworkingwith/Workingwithfunctionsself-invokingfunction,using/Self-invokingfunctionsandclosuresclosures,using/Self-invokingfunctionsandclosureshigher-orderfunctions/Higher-orderfunctionspurefunctions/Purefunctionsanonymousfunctions/Anonymousfunctionsmethods,chaining/Methodchainsrecursivefunction/Recursionlazyevaluation/Lazyevaluation

functorsabout/Functorscreating/Creatingfunctorsfunctioncompositions/Functioncompositions,revisited

Gglobalscope,variables

about/Globalscope

HHaskell

about/Whatmakesalanguagefunctional?higher-orderfunctions/Higher-orderfunctionshomomorphicoperations

about/Categorytheoryinanutshell

Iidentityfunctionmorphism/Arraysandfunctorsinheritance

about/InheritancewithObject.create()method/InheritanceinJavaScriptandtheObject.create()method

JJavaScript

about/Introduction,IsJavaScriptafunctionalprogramminglanguage?recursion/Recursionvariablescope/Variablescopefunctiondeclarations/Functiondeclarationsversusfunctionexpressionsversusthefunctionconstructorfunctionexpressions/Functiondeclarationsversusfunctionexpressionsversusthefunctionconstructorfunctionconstructor/Functiondeclarationsversusfunctionexpressionsversusthefunctionconstructormulti-paradigmlanguage/JavaScript–themulti-paradigmlanguageobject-orientedimplementation/JavaScript’sobject-orientedimplementation–usingprototypesversusC++/JavaScript’sobject-orientedimplementation–usingprototypes

jQueryabout/Introduction

jQueryobjectabout/jQueryisamonadimplementing/jQueryisamonad

Juliaabout/Introduction

LLazy.js/Lazy.jslazyevaluation

about/Lazyevaluationbenefits/Lazyevaluation

lens()functionwriting/Lenses

lensesabout/Lenses

LINQ(LanguageIntegratedQuery)about/Honorablementions

Lispabout/Whatmakesalanguagefunctional?

localscope,variablesabout/Localscope

Mmap()function

parameters/Array.prototype.map()maybes

about/Maybeswriting/Maybes

memoizationabout/Memoizationreferencelink/Memoization

mixinsabout/Mixinsclassicalmixins/Classicalmixinsfunctionalmixins/Functionalmixins

monadsabout/Monadsmaybes/Maybespromises/Promiseslenses/LensesjQueryobject/jQueryisamonad

morphismsabout/Categorytheoryinanutshell,Typesafety

MVP(model-view-provider)/UsingfunctionallibrarieswithotherJavaScriptmodules

Oobject-orientedimplementation,JavaScript

prototypes,using/JavaScript’sobject-orientedimplementation–usingprototypesinheritance/Inheritanceprototypechain/JavaScript’sprototypechaininheritance,withObject.create()method/InheritanceinJavaScriptandtheObject.create()method

object-orientedprogrammingandfunctionalprogramming,mixing/Mixingfunctionalandobject-orientedprogramminginJavaScript

Object.create()methodusing/InheritanceinJavaScriptandtheObject.create()method

objectproperties,variablesabout/Objectproperties

objectsabout/Typesafety

ƒogsymbolabout/Categorytheoryinanutshell

Ppartialapplication

about/Partialfunctionapplicationandcurrying,Partialapplicationleftarguments,applying/Partialapplicationfromtheleftrightarguments,applying/Partialapplicationfromtheright

polyadicabout/Functioncomposition

polymorphicoperationsabout/Categorytheoryinanutshell

productionenvironmentabout/Developmentandproductionenvironments

promisesusing/Promises

Promises/A+implementation/Promisesprototypechain

about/JavaScript’sprototypechainprototypes

using,forinheritance/JavaScript’sobject-orientedimplementation–usingprototypes

purefunctions/PurefunctionsPyjs/FunctionallanguagesthatcompileintoJavaScriptPython

about/Introduction

QQuickCheck

about/Bilby.js

Rrecursion

about/RecursionY-Combinator/TheY-combinator

recursivefunctionabout/RecursionDivideandConquer/Divideandconquer

reduce()functionparameters/Array.prototype.reduce()

Roy/FunctionallanguagesthatcompileintoJavaScriptRuby

about/Introduction

SScalaCheck

about/Bilby.jsScheme

about/Whatmakesalanguagefunctional?scoperesolutions

about/Scoperesolutionsglobalscope/Globalscopelocalscope/Localscopeobjectproperties/Objectproperties

self-invokingfunctionusing/Self-invokingfunctionsandclosures

server-sideJavaScriptfunctionalusecase/Afunctionalusecaseintheserver-sideenvironment

StrategyPatternabout/StrategyPattern

Ttail-callelimination

about/TheTail-calleliminationtrampolining/Trampolining

tailrecursionabout/Tailrecursiontail-callelimination/TheTail-callelimination

ternaryabout/Functioncomposition

thiskeyword/Apply,call,andthethiskeywordthunks

about/Trampoliningtoolkit,functionalprogrammer

about/Thefunctionalprogrammer’stoolkitcallbacks,using/CallbacksArray.prototype.map()/Array.prototype.map()Array.prototype.filter()/Array.prototype.filter()Array.prototype.reduce()/Array.prototype.reduce()Array.prototype.forEach/Array.prototype.forEachArray.prototype.concat/Array.prototype.concatArray.prototype.reverse/Array.prototype.reverseArray.prototype.sort/Array.prototype.sortArray.prototype.every/Array.prototype.everyandArray.prototype.someArray.prototype.some/Array.prototype.everyandArray.prototype.some

Trampoliningabout/IsJavaScriptafunctionalprogramminglanguage?

trampoliningabout/Trampolining

TypeScript/FunctionallanguagesthatcompileintoJavaScript

UUHC/FunctionallanguagesthatcompileintoJavaScriptUnaryfunctions

about/Functioncompositionunderscore.js

about/IntroductionUnderscore.js/Underscore.js

Vvariablescope

about/Variablescopescoperesolutions/Scoperesolutionsissues/Closuresfeatures/Gotchas

variadicabout/Functioncomposition

YY-Combinator

about/TheY-combinatormemoization/Memoization