Is de Opmars van Functioneel Programmeren Bij

advertisement
IS DE OPMARS VAN FUNCTIONEEL PROGRAMMEREN BIJ
WEB DEVELOPMENT MEER DAN EEN RECENTE GRIL?
Promotor: MH Kevin DeRudder
Onderzoeksvraag uitgevoerd door
PIETER-JAN VANDENBUSSCHE
Voor het behalen van de graad van Bachelor in de
NEW MEDIA AND COMMUNICATION TECHNOLOGY
Howest | 2015-2016
IS DE OPMARS VAN FUNCTIONEEL PROGRAMMEREN BIJ
WEB DEVELOPMENT MEER DAN EEN RECENTE GRIL?
Promotor: MH Kevin DeRudder
Onderzoeksvraag uitgevoerd door
PIETER-JAN VANDENBUSSCHE
Voor het behalen van de graad van Bachelor in de
NEW MEDIA AND COMMUNICATION TECHNOLOGY
Howest | 2015-2016
Mijn naam is Pieter-Jan Vandenbussche, student New Media & Communication Technology
(NMCT) Howest, campus Kortrijk. Gedurende de middelbare school had ik al een interesse in
programmeren en dergelijke, maar omwille van een lage motivatie in die periode – toen een
van mijn mindere eigenschappen – ben ik er ook niet verder op ingegaan.
Na afgestudeerd te zijn van de middelbare school met een diploma Latijn-Moderne Talen en
geen benul te hebben over welke carrière wil ik nastreven in de – toen – nabije toekomst,
begon ik aan mijn korte periode als student in de Rechten. Te laat bleek het dat mijn interesse
in dit veld stilaan onder nul begon te dippen en dus besloot ik – jammer genoeg voor mijn
ouders – na één jaar om het voor gezien te houden.
In de weken na mijn laatste semester als rechtenstudent ben ik – na wat grondig ‘soulsearching’ – tot de realisatie gekomen dat ik een carrière in development wil nastreven.
Toentertijd kende ik reeds iemand die net zijn eerste jaar NMCT had vervolledigd en die een
lovende opinie had over de opleiding. Uiteindelijk – na wat vergelijking met andere scholen
– er toch voor gekozen om mijn diploma te behalen aan Howest, studierichting NMCT.
Mijn interesse in alles omtrent programmeren begon serieus te groeien tijdens deze laatste
drie jaren, met een nadruk op web development, front – en backend. Buiten de lessen was ik
altijd op zoek naar nieuwe interessante topics, en functioneel programmeren was altijd al
een van die topics waar ik graag wat dieper op zou willen ingaan. Toen het nieuws kwam dat
we een onderwerp dienden te zoeken voor een bachelorproef, was de keuze vlug gemaakt.
Het bleek dat 2 van mijn interesses, web development en functioneel programmeren
(waarover ik toen een zeer gelimiteerde kennis had), een zekere toekomst hadden, of zo zag
het er toch zo uit, volgens meerdere bronnen. Tot zo ver de keuze van mijn onderzoeksvraag.
Graag zou ik Kevin DeRudder, mijn promotor, en Ann Deraedt bedanken voor hun
begeleiding gedurende het schrijven van deze bachelorproef. Verder wil ik alle lectoren die
mij deze voorbije drie jaren onderwezen hebben bedanken voor hun inzet. Tot slot wil ik mijn
ouders bedanken die mij constant motiveren om het beste van mezelf te geven en die mij de
kans gegeven hebben om – na een wat ongelukkig eerste jaar als student – te starten in
NMCT.
De laatste tijd worden concepten van functioneel programmeren meer en meer toegepast bij
web development, specifieker nog, in JavaScript. Deze bachelorproef is een onderzoek naar
waarom functioneel programmeren in web development populairder wordt en of die recente
populariteit niets meer is dan een recente gril zonder enige serieuze voordelen.
Het onderzoek zal beginnen met een inleiding te geven wat functioneel programmeren nu
juist is en redenen waarom men functioneel programmeren in het algemeen zou gebruiken.
Hierna worden de faciliteiten onderzocht die JavaScript standaard aanlevert waarmee men in
een zekere functionele programmeerstijl kan werken. Vervolgens wordt er ingegaan op
verschillende libraries, frameworks en dergelijke die concepten uit functioneel
programmeren implementeren in JavaScript. Ten slotte wordt er onderzocht waarom men
juist concepten uit het functioneel programmeren zou gebruiken in JavaScript en wordt er
een conclusie gegeven, namelijk dat deze recente populariteit van functioneel programmeren
in JavaScript wel degelijk meer is dan een recente gril. Hoe complexer web applicaties worden
hoe moeilijker het wordt voor de developer. Concepten uit het functioneel programmeren
helpen de developer om die complexiteit beter te benaderen en uit te werken.
Lijst met afkortingen.................................................................................................................................. 7
Verklarende woordenlijst ......................................................................................................................... 8
Inleiding ........................................................................................................................................................ 9
1.
2.
3.
Functioneel Programmeren............................................................................................................ 10
1.1.
Wat is functioneel programmeren? ..................................................................................... 10
1.2.
Hogere-ordefuncties (Higher order functions) ................................................................. 12
1.3.
Luie evaluatie (Lazy evaluation) ........................................................................................... 13
1.4.
Waarom functioneel programmeren? ................................................................................. 14
1.4.1.
Voordelen .........................................................................................................................................14
1.4.2.
Nadelen .............................................................................................................................................17
1.4.3.
Conclusie ..........................................................................................................................................18
Functioneel programmeren in JavaScript ................................................................................... 18
2.1.
Recente populariteit functioneel programmeren.............................................................. 18
2.2.
Wat is functioneel programmeren in JavaScript? ............................................................ 19
2.2.1.
Higher order functions .................................................................................................................19
2.2.2.
Itereren over arrays (map, filter, reduce) .................................................................................20
2.2.3.
Function composition....................................................................................................................23
2.2.4.
Recursie .............................................................................................................................................24
2.2.5.
Luie evaluatie (lazy evaluation) ...................................................................................................25
Implementaties in JavaScript ......................................................................................................... 25
3.1.
Lodash, Ramda en Underscore .............................................................................................. 26
3.2.
React en Redux ......................................................................................................................... 29
3.3.
ImmutableJS .............................................................................................................................. 31
4.
Waarom functionele stijl in JavaScript?...................................................................................... 32
5.
Conclusie ............................................................................................................................................ 34
Literatuurlijst ............................................................................................................................................. 35
6
OOP
Object Oriented Programming
MVC
Model View Controller
UI
User Interface
HTML
Hyper Text Markup Language
JSX
JavaScript Serialization to XML
DOM
Document Object Model
AJAX
Asynchronous JavaScript and XML
7
Quicksort
Een veelgebruikt algoritme om een lijst, array, etc. te sorteren.
NodeJS
Een softwareplatform waarop men applicaties geschreven in JavaScript
kan ontwikkelen.
Call stack
Een data structuur van de JavaScript engine die calls naar functies
bijhoudt in het geheugen tot er een return statement volledig uitgevoerd
is in die functie
AngularJS
Webapplicatieframework voor Single Page Applications
Ember
Webapplicatieframework voor Single Page Applications
8
De laatste tijd worden concepten van functioneel programmeren meer en meer toegepast bij
web development. Deze bachelorproef is een onderzoek naar waarom functioneel
programmeren in web development populairder wordt en of die recente populariteit wel
gerechtvaardigd is. M.a.w., welke problemen – bij het ontwikkelen van web applicaties – lost
het gebruik van concepten uit functioneel programmeren al dan niet (beter) op, vergeleken
met imperatief programmeren? Op welke vlakken kan denken in concepten van functioneel
programmeren de (frontend en/of backend) developer al dan niet helpen bij het ontwikkelen
van een web applicatie? Zorgt het toepassen van bovenstaande concepten ervoor dat je een
beter overzicht hebt over de werking en/of flow van je applicatie, zorgt het toepassen van
bovenstaande concepten ervoor dat je efficiënter gaat werken bij complexere codebases? Is er
enig verschil in productiviteit wanneer functioneel programmeren in JavaScript gebruikt
wordt in teamverband? Een al dan niet positief antwoord op elk van deze vragen volgt.
Er wordt verwacht dat de lezer van deze bachelorproef over enige basiskennis JavaScript en
programmeren in het algemeen bezit.
9
Functioneel programmeren is een programmeerparadigma die, zoals de naam doet
vermoeden, grotendeels gebaseerd is op het werken met functies of expressies om applicaties
te construeren. Deze functies zijn gelijkaardig aan de gekende wiskundige functies, en nog
specifieker, hebben als belangrijkste inspiratie de lambda calculus. Zonder al teveel in detail
te gaan, de lambda calculus is voorgesteld in 1930 door Alonzo Church als een systeem om
onderzoek te doen naar berekenbare functies. (Allen & Moronuki, 2015)
Om een wat duidelijker beeld te geven op wat functioneel programmeren juist is, is het beter
om het te vergelijken met een meer gekend programmeerparadigma, namelijk imperatief
programmeren. Enkele voorbeelden van imperatieve talen zijn bijv. C, Java, Go etc. Deze talen
zijn allemaal gekend als imperatieve talen omdat ze op een imperatieve manier, met een
gebiedende wijs, een programma definiëren als een sequentiële opvolging van statements die
in éénzelfde volgorde ook geëxecuteerd worden. Deze statements worden dan gebruikt om de
state van een programma te manipuleren. Anders geformuleerd, imperatieve talen
beschrijven hoe een programma functioneert. (Wikipedia, 2016)
Het vinden van de som van alle nummers gevonden in een array kan als voorbeeld genomen
worden om bovenstaande te verduidelijken:
//C#
//veronderstel: array = [1, 2, 3, 4, 5]
int som = 0;
for (int i = 0; i < array.length; i++) {
som += array[i];
}
In deze code is er een state, namelijk de variabele som die elke iteratie van de for-loop
gemuteerd wordt. Verder is het duidelijk dat er een opeenvolging van commando’s is,
namelijk elke iteratie, tijdens de executie van deze code.
Functioneel programmeren daarentegen zal niet beschrijven hoe een programma
functioneert, maar zal eerder beschrijven wat een programma is en welke problemen en
deelproblemen het oplost. Het zal beschrijven hoe data getransformeerd wordt en nieuwe
data geproduceerd wordt door het gebruik van expressies gebaseerd op de eerder vermelde
lambda calculus. Net zoals deze mathematische functies gespecifieerd in de lambda calculus
nemen functies in een functionele programmeertaal een aantal argumenten en geven ze een
nieuwe waarde terug na het eventuele transformeren van de verkregen argumenten. Deze
functies zullen altijd dezelfde waarde teruggeven wanneer dezelfde argumenten worden
aangeleverd. Dit soort functies worden ook wel eens pure functies genoemd. (Wikipedia,
2016)
10
Omdat programma’s geschreven in een functionele programmeertaal bestaan uit exclusief
expressies – code die altijd tot een nieuwe waarde evalueert – is het nagenoeg onmogelijk –
er zijn uitzonderingen – om bestaande variabelen te muteren. Dit is ook wel gekend als
‘immutability’. In combinatie met het concept van pure functies zorgt het voorgaande ervoor
dat er geen bijwerkingen ontstaan wanneer een functie uitgevoerd wordt. Met bijwerkingen
wordt er in deze context bedoeld dat wanneer een functie meerdere keren uitgevoerd wordt
binnen een programma, de staat waarin dit programma verkeert altijd exact hetzelfde blijft.
Het merendeel van de functionele programmeertalen laat de programmeur toe om in kleinere
mate bijwerkingen te hebben, maar het ultieme streefdoel is toch uiteindelijk geen
bijwerkingen (eg. Haskell).
Ter verduidelijking, de equivalent van het vorige codevoorbeeld geschreven in een
functionele programmeertaal:
-- Haskell
-- veronderstel: array = [1, 2, 3, 4, 5]
som = sum(array)
De functie sum neemt 1 argument en geeft de som van alle waarden terug. Er zijn geen
bijwerkingen en er worden geen waarden gemuteerd, alleen maar geïnitialiseerd. Merk op dat
de taal zelf de developer een abstractie geeft – namelijk sum – en ervoor zorgt dat de developer
zich geen zorgen moet maken over hoe de som van een array gevonden wordt.
Dit alles in tegenstelling tot imperatieve talen waar bijwerkingen bij functies perfect mogelijk
zijn:
//C#
int nummer = 1
public int functieMetBijwerkingen(n) {
nummer++;
return nummer * n;
}
functieMetBijwerkingen(5); // = 10
functieMetBijwerkingen(5); // = 15
Aangezien de meeste talen over expressies beschikken kan de aandachtige lezer opmerken
dat het niet onmogelijk is om het merendeel van de tijd zonder bijwerkingen en mutaties van
variabelen te werken met imperatieve programmeertalen. Dit is een correcte opmerking,
maar, proberen om in een functionele stijl in een van deze talen te werken geeft de developer
11
een veel minder aangename ervaring dan in een functionele programmeertaal, aangezien deze
een aantal features aanbieden die ervoor zorgen dat een functionele programmeertaal toch
uniek is. Dit zijn features zoals hogere-ordefuncties (higher order functions), luie evaluatie
(lazy evaluation) en recursie. Er zijn nog vele meerdere aspecten van het functionele
programmeren die het uniek maken – pattern matching, abstract data structures, etc. - maar
deze zijn buiten het bereik van deze scriptie of minder relevant voor het volgende hoofdstuk.
(Hudak, 1989)
Hogere-ordefuncties zijn functies die als argument één of meerdere functies kunnen nemen
en/of een functie teruggeven. Dit is mogelijk in functionele programmeertalen omdat functies
gezien worden als first-class citizens, m.a.w., in de ogen van een functionele programmeertaal
zijn functies equivalent aan bijvoorbeeld een nummer. Zoals een nummer kan doorgegeven
worden als argument aan een functie, teruggegeven kan worden door een functie en
toegewezen kan worden aan variabele, zo ook kan dit allemaal met een functie gebeuren.
(Lipovača, 2016)
Ter verduidelijking, een voorbeeld van een hogere-ordefunctie:
-- Haskell
plusEen x = x + 1
resultaat = map plusEen [1, 2, 3, 4, 5]
-- [2, 3, 4, 5, 6]
plusEen is een functie die 1 argument neemt, namelijk x en die simpelweg 1 bij x optelt en
vervolgens het resultaat teruggeeft. Map is in dit geval de hogere-ordefunctie die plusEen als
argument neemt en deze toepast op alle waarden uit de lijst [1, 2, 3, 4, 5].
Dit concept van hogere-orde functies zorgt ervoor dat men gebruik kan maken van partial
application. Dit wil zeggen dat wanneer men aan een functie die bijv. 2 argumenten verwacht
maar 1 argument meegeeft, er een functie wordt teruggeven die als argument het eerder niet
gegeven tweede argument verwacht. Dit wordt o.a. toegepast om acties of functionaliteiten
die meerdere keren plaats kunnen vinden te abstraheren en deze vervolgens partieel te
appliqueren om nieuwe functies te creëren (Wiki.Haskell, 2016) Bijvoorbeeld :
-- Haskell
plusTwee x = x + 2
plusDrie x = x + 3
-----------------plus n x = n + x
plusTwee = plus 2
12
plusDrie = plus 3
plusTwee 3 -- 5
Hier zijn er in de bovenste helft twee functies gedefinieerd, namelijk plusTwee en plusDrie. Ze
nemen beide een argument x en tellen daarbij 2 of 3. Het is duidelijk dat beide functies
éénzelfde actie uitvoeren (optellen), maar simpelweg met andere getallen. Partial application
laat ons toe om dit gedrag te abstraheren, namelijk in de functie plus. Deze functie neemt 2
getallen en telt deze op. Wanneer er vervolgens maar 1 argument wordt toegepast op plus,
geeft deze een functie terug die een argument verwacht om deze uiteindelijk op te tellen bij
het originele argument.
Hogere-ordefuncties en het abstraheren van gemeenschappelijke gedragen zorgt ervoor dat
applicaties korter geschreven kunnen worden en zorgen er ook voor dat de code simpeler is
om te begrijpen, aangezien ten eerste de actie duidelijker omschreven kan worden
(bijvoorbeeld bij een functie zoals map zie je duidelijk dat er een bepaalde functie zal toegepast
worden op een lijst terwijl dat bij een simpele for-lus de intentie van de code vaak niet
compleet duidelijk is) en ten tweede, omdat functies bij functionele programmeertalen
functies als paramater kunnen nemen, heeft dit als gevolg dat ze herbruikbaarder zijn dan bij
imperatieve programmeertalen. (Erlang, 2016)
Wanneer in o.a. een imperatieve taal code wordt geëvalueerd, worden alle expressies
onmiddellijk geëvalueerd, bijvoorbeeld:
//pseudocode
function delen(teller, noemer) {
if (noemer == 0) return null;
return teller / noemer;
}
delen((5+2), 0);
In dit voorbeeld zal de expressie (5+2) geëvalueerd worden nog voor de body van de functie
wordt uitgevoerd. Dit heet eager evaluation, eager in de zin dat expressies gretig worden
geëvalueerd wanneer deze in de loop van de executie van een programma aangetroffen
worden.
In een functionele taal met lazy evaluation – niet alle functionele talen hebben lazy evaluation
– worden expressies maar geëvalueerd eens ze nodig zijn, ook wel gekend als call-by-need.
Indien bovenstaande pseudocode uitgevoerd zou worden in een functionele taal zoals Haskell
dan zou (5+2) maar geëvalueerd worden eens de waarde daadwerkelijk nodig is bij teller /
noemer, wat in bovenstaand code voorbeeld niet eens het geval is. (Wikipedia, 2016)
13
Het hebben van lazy evaluation is maar realistisch en bruikbaar in een taal waar men vrijwel
alleen maar werkt met pure functies. Wanneer er namelijk met pure functies gewerkt wordt,
heeft de volgorde waarin expressies geëvalueerd worden geen gevolg op het uiteindelijke
resultaat. Bij een imperatieve taal daarentegen kan men dit niet garanderen omwille van de
eventuele aanwezigheid van bijwerkingen (side-effects). (Delft University of Technology,
2014)
Deze manier van evalueren brengt enkele voordelen met zich mee. Ten eerste voorkomt het
onnodige evaluaties van expressies, ten tweede geeft het de developer de mogelijkheid om te
werken met oneindige lijsten en ten derde, omdat het nooit meer werk doet dan noodzakelijk,
wordt je code meer modulair. (Delft University of Technology, 2014)
Om het eerste voordeel te verduidelijken staat hier de implementatie van het QuickSort
algoritme in Haskell geschreven (Eidhof, 2008):
-- Haskell
quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs)
++ [x]
++ quickSort (filter (>= x) xs)
minimum ls = head (quickSort ls)
De implementatie van het algoritme op zich is hier niet van belang, buiten het feit dat bij
QuickSort de gegeven list recursief in twee wordt gesplitst op basis van een pivot (hier
voorgesteld door x) en zo gesorteerd wordt. In de minimum functie wordt de functie head
gebruikt, die, gegeven een list, het eerste element teruggeeft. Omdat Haskell lazy evaluation
gebruikt, en men alleen maar het eerste element nodig heeft uit de – nog niet bestaande –
finale gesorteerde lijst zal telkens maar het eerste deel van de quickSort functie (aangeduid
met pijl) uitgevoerd worden tot het uiteindelijk het eerste element vindt. In een taal met eager
evaluation zou eerst de gehele lijst gesorteerd worden om vervolgens het eerste element te
nemen.
Natuurlijk heeft dit ook zijn nadelen. Aangezien je niet altijd weet welke expressie wanneer
geëvalueerd zal worden, is het moeilijk om te redeneren over het aantal geheugen dat een
applicatie zou gebruiken met lazy evaluation. Dit zorgt er, in het grote merendeel van de
gevallen, niet voor dat je code minder performant is, maar het is goed om te weten.
Er moet ook rekening gehouden worden met het feit dat – zoals al eerder werd aangehaald –
lazy evaluation de developer dwingt om volledig puur (zonder side-effects) te werken.
(University of Pennsylvania, 2014)
14
Zoals eerder al is uitgelegd, is het concept van het toewijzen van een waarde aan een variabele
in een functionele programmeertaal onbestaand. Bij een expressie als deze wordt de waarde
5 niet toegewezen aan x d.m.v. =.
x = 5
Het gelijk-aanteken betekent in dit geval dat het linker- en rechtergedeelte verwisselbaar zijn,
ze zijn volledig gelijk aan elkaar. Het is dan ook logisch dat de variabele x onveranderlijk (of
‘immutable’) is, aangezien we expliciet vermelden in de code dat x gelijk is aan 5, op een zelfde
manier dat 1 = 1. Dit heeft als gevolg dat een evaluatie in eender welke context altijd naar
dezelfde waarde evalueert, een concept dat ook wel gekend is als ‘referential transparency’.
Bovenstaande in combinatie met de notie van pure functies in functionele programmeertalen
vermindert het risico op bugs of dergelijke op een drastische manier. Je zou zelfs kunnen
stellen dat het merendeel van de bugs die gevonden worden in applicaties een direct gevolg
zijn van functies met bijwerkingen. Met functionele talen heeft de developer absolute
zekerheid in welke staat een programma op een gegeven moment zich begeeft. Lazy
evaluation op zijn beurt elimineert de nood aan een vorm van flow control die men normaal
gezien nodig heeft bij een imperatieve taal. (Haskell Wiki, 2016)
Ook zijn eenzelfde programma’s geschreven in een functionele taal gemiddeld 2 tot wel 10
keer korter dan een variant geschreven in een imperatieve taal. Dit haakt terug op het eerder
vermelde verschil tussen imperatieve en functionele programmeertalen, namelijk dat
functionele talen de nadruk leggen op wat een programma zal berekenen in plaats van hoe
een programma iets zal berekenen. Functionele talen zijn in dit opzicht ‘high-level languages’,
waarmee bedoeld wordt dat functionele talen zeer veel handige functionaliteit abstraheren
van de machine taal die uiteindelijk door de computer uitgevoerd wordt, veel meer dan
imperatieve talen die gaande van C – zeer low-level, weinig abstractie – tot een taal zoals C#,
in zekere zin nog altijd wat abstractie hebben, maar niet op hetzelfde niveau als functionele
programmeertalen. Code geschreven in een functionele taal leest eerder als een algoritme dan
een stel instructies gegeven aan de computer. Bijvoorbeeld, hier volgt, opnieuw, het
QuickSort algoritme, maar dan geschreven in C++ en Haskell, door de Haskell Wiki (Haskell
Wiki, 2016):
//C++
template <typename T>
void qsort (T *result, T *list, int n)
{
if (n == 0) return;
T *smallerList, *largerList;
smallerList = new T[n];
largerList = new T[n];
T pivot = list[0];
int numSmaller=0, numLarger=0;
for (int i = 1; i < n; i++)
if (list[i] < pivot)
15
smallerList[numSmaller++] = list[i];
else
largerList[numLarger++] = list[i];
qsort(smallerList,smallerList,numSmaller);
qsort(largerList,largerList,numLarger);
int pos = 0;
for ( int i = 0; i < numSmaller; i++)
result[pos++] = smallerList[i];
result[pos++] = pivot;
for ( int i = 0; i < numLarger; i++)
result[pos++] = largerList[i];
delete [] smallerList;
delete [] largerList;
};
-- Haskell
qsort []
= []
qsort (x:xs) = qsort less ++ [x] ++ qsort more
where less = filter (<x) xs
more = filter (>=x) xs
Merk op dat de C++ code stukken langer en complexer is dan de Haskell code. Een developer
die nog nooit QuickSort heeft gezien zal a.d.h.v. de C++ code niet onmiddellijk snappen hoe
dit algoritme werkt, mede omdat de taal nog altijd omgaat met enkele low-level features. Men
kan vaag afleiden uit de code welke stappen de computer gaat ondernemen ‘under the hood’,
i.p.v. zich volledig op de probleemstelling te richten.
De Haskell code daarentegen is stukken korter en toont de developer wat het berekent i.p.v.
in detail te gaan over hoe het resultaat berekent wordt. Ook is de code te begrijpen in één
oogopslag, dit door de eerder al vermelde high-level abstracties.
Dezer tijden wordt computer snelheid ook almaar goedkoper, terwijl de tijd dat een developer
spendeert aan een project juist duurder wordt. Programma’s geschreven in een functionele
programmeertaal zijn dan misschien wel niet de meest performante programma’s – ze zijn
ook niet de minst performante – maar worden in een veel kortere tijd geproduceerd, wat
uiteraard voordelig is voor de klant. Met andere woorden, hoe meer men streeft naar een
hoger level van abstractie, hoe productiever de developer wordt. (Ford, 2013)
Verder is concurrency (programmeren op meerdere threads) almaar belangrijker aan het
worden. In vele imperatieve talen is het een ongelooflijke nachtmerrie om te werken met
concurrency en tegelijkertijd een duidelijk overzicht te hebben van de flow van data doorheen
je applicatie, aangezien de developer altijd op zijn hoede moet zijn omtrent data en de huidige
16
state ervan. Immutability, pure functies etc., concepten die standaard beschikbaar zijn in een
functionele programmeertaal, maken het leven van de developer stukken gemakkelijker bij
het werken met concurrency of parellelisme.
Een laatste reden om in een functionele stijl te programmeren is hoe sterk modulariteit
aanwezig is. Modulariteit niet alleen in de zin van verschillende modules, maar ook hoe
eenvoudig het is om probleem op te delen in – eventueel herbruikbare – deelproblemen.
Waarom is een functionele taal dan zo geschikt om aan modulair programmeren te doen?
Volgens John Hughes is de reden als volgt:
Languages which aim to improve productivity must support modular programming
well. But new scope rules and mechanisms for separate compilation are not enough modularity means more than modules. Our ability to decompose a problem into parts
depends directly on our ability to glue solutions together. To assist modular
programming, a language must provide good glue.
Functional programming languages provide two new kinds of glue - higher-order
functions and lazy evaluation. (Hughes, 1984)
Indien we nu eerst even een stap terugnemen en een programma geschreven in een
functionele taal op een wat abstractere wijze observeren, blijkt het dat deze één grote functie
is die een input neemt en een output produceert. Wanneer dit programma terug onder de
loep genomen wordt, valt op dat deze volledig opgebouwd is uit verschillende functies die op
hun beurt ook opgebouwd zijn uit verschillende functies, allemaal samengehouden door de
zogenaamde ‘glue’ van het functionele programmeren, higher-order functions en lazy
evaluation.
Zo kan een developer een applicatie continu opsplitsen in meerdere deelproblemen tot een
deelprobleem niet verder dient opgesplitst te worden omdat deze zich volledig focust op één
enkel, klein en handelbaar probleem dat vertaalt kan worden in een korte en pure functie.
Later kunnen al deze deelproblemen samengesteld worden met behulp van de zogenaamde
‘glue’ – ook wel composing genaamd – om zo uiteindelijk een resultaat te bekomen dat als
antwoord dient op het oorspronkelijke probleem dat de applicatie voorop bracht. Of zoals
Rich Hickey ooit vermelde tijdens een presentatie: “Composing simple components, simple
in that same respect, is the way we write robust software.” (Hickey, 2011). Components in de
zin van deelproblemen die volledig individueel capabel zijn en niet afhankelijk zijn van andere
deelproblemen.
Zoals alles komt functioneel programmeren natuurlijk ook met zijn nadelen. Een daarvan is
al eerder aangehaald bij het onderdeel over lazy evaluation, namelijk dat het zeer moeilijk is
om op voorhand te weten hoeveel geheugen of tijd een programma geschreven in een
functionele taal exact nodig heeft. Ook is functioneel programmeren minder geschikt voor
embedded systems of andere omgevingen waar het nodig is om low-level te gaan werken,
17
omdat ten eerste – zoals eerder vermeld – vrijwel alles low-level is geabstraheerd in een
functionele taal, en ten tweede een functionele programmeertaal niet het model van de
werking van een computer volgt. Deze werking is fundamenteel imperatief, in die zin dat het
state heeft en met elke instructie deze state aangepast wordt. Als gevolg is het dan ook veel
moeilijker om de snelheid van een in een functionele taal geschreven programma te
optimaliseren zoals een imperatieve taal als C of C++ dit zou kunnen doen. (Duchene, 2014)
Uiteindelijk is de instapdrempel van een functionele programmeertaal vrij hoog voor de
gemiddelde developer die vrijwel zijn gehele carrière met imperatieve talen gewerkt heeft.
Niet alleen omdat het een nieuwe syntax heeft maar, belangrijker, omdat vele concepten
toegepast in een imperatieve taal – zelfs iets simpel als de notie van variabelen – volledig
opnieuw aangeleerd moeten worden. Ook moet er natuurlijk rekening gehouden worden met
het feit dat de huidige ecosystemen van de meeste functionele programmeertalen niet kunnen
tippen aan die van talen zoals Java op gebied van open source libraries en dergelijke. (Jelvis,
2014)
Samengevat, voor het merendeel van de programmeurs – die niet werken met embedded
systems of dergelijke – is het voordeliger om een functionele taal te leren, ongeacht de initiële
instapdrempel, omdat de productiviteit van de programmeur en de onderhoudbaarheid en
stabiliteit van een programma er alleen maar vooruit opgaan in een functionele omgeving
vergeleken met een imperatieve omgeving, vooral in teamverband. Het feit dat functionele
talen minder performant zijn is wel degelijk nog altijd aanwezig, maar de almaar goedkopere
aanwezigheid van computersnelheid en het almaar duurder worden van developer tijd zorgt
er voor dat de voordelen van een functionele taal harder doorwegen dan de nadelen.
Maar op het einde van de dag is er niks absoluut en moet er nog altijd gekozen worden op
basis van het probleem, en niet wat er momenteel het meest populair is.
Voor velen is JavaScript samen met C# of Java een van de eerste talen die ze leren. De lessen,
tutorials, etc. die beginners nemen voor deze talen leggen vrijwel altijd de nadruk op een
imperatieve stijl of nog specifieker een OOP stijl – ook al is JavaScript vrij arm op het gebied
van OOP – en krijgen maar veel later de memo dat JavaScript meer dan geschikt is om in een
functionele stijl te gaan programmeren. De syntax is dan wel gebaseerd op C, conceptueel
gezien lijkt JavaScript meer op functionele programmeertalen zoals Lisp of Scheme.
(Wikipedia , 2016)
Recent is het zo dat functioneel programmeren in JavaScript steevast meer populariteit geniet
van web developers, frontend en backend (in het geval van NodeJS). Deze opmars heeft
meerdere oorzaken. Een ervan kan toegewijd worden aan het team achter JavaScript, en nog
18
specifieker ECMAScript, Ecma International. Dit is simpel gezegd een groep die de
standaarden opmaakt omtrent de syntax en implementatie van de taal. Historisch gezien
volgden aanpassingen aan ECMAScript zich zeer traag op – tot zelfs 10 jaar tussen
ECMAScript 3 en ECMAScript 5 (ECMAScript 4 is geschrapt geweest) – maar hier is
ondertussen al enig schot in gekomen, met een geplande nieuwe versie elk jaar. Sinds
ECMAScript 5 zijn het merendeel van de nieuwe standaarden gericht op een functionele
programmeerstijl, met een nog grotere nadruk in ECMAScript 6.
Een tweede oorzaak van deze recente populariteit zijn libraries zoals Lodash, Underscore &
Ramda die veel functionaliteit abstraheren in meerdere handige functies met een focus op
functioneel programmeren.
En een derde maar niet laatste oorzaak is de opmars van React. React op zichzelf is zeer
populair geworden in vrij korte tijd, en mede door hype zijn enorm veel web developers op
de spreekwoordelijke React-trein gesprongen. De implementatie van React op zich echter is
verrassend vol van functionele concepten, zo ook zijn er enkele libraries die het omgaan met
data bij o.a. React definiëren – zoals Redux – die gebaseerd zijn op bestaande functionele
programmeertechnieken. Op deze manier worden veel programmeurs geïntroduceerd tot de
wereld van functioneel programmeren.
Voor er dieper wordt ingegaan op bovenstaande implementaties, moet het volgende wat
nader verklaard worden, namelijk, wat houdt functioneel programmeren in JavaScript nu juist
in?
Net omdat JavaScript – zoals eerder al is vermeld – gebaseerd is op functionele
programmeertalen zoals Lisp en Scheme, heeft het enkele eigenschappen die ervoor zorgen
dat men vrijwel zonder enige nood aan externe libraries in een functionele stijl applicaties
kan creëren. Let wel op, het is niet de bedoeling in dit hoofdstuk om aan te tonen dat
JavaScript gebruikt kan worden als een volwaardige functionele programmeertaal zoals
Haskell dat is. Het is echter wel de bedoeling om aan te tonen dat JavaScript een taal is die
het mogelijk maakt om met meerdere concepten uit het functioneel programmeren te werken.
De hele reden waarom een functionele programmeerstijl mogelijk is in JavaScript, is het feit
dat functies gezien worden als first-class variables.
var plus = function (a, b) {
return a + b;
}
console.log(plus); //[Function]
Logischerwijze kunnen we hieruit afleiden dat JavaScript ook kan werken met higher order
functions, één en dezelfde higher order functions als in het voorgaande hoofdstuk.
19
Ondertussen zien we ook onmiddellijk dat JavaScript anonymous functions heeft, of anders
genaamd, lambdas!
var plus = function (a) {
return function (b) {
return a + b;
}
}
var plus2 = plus(2);
plus2(4) //6
Uit dit voorbeeld kunnen we nog een bouwstuk halen die JavaScript bezit om in een
functionele manier te gaan programmeren, namelijk closures. Wanneer de functie plus
gecalled met waarde 2 zal deze een functie teruggeven die deze waarde in het geheugen houdt,
tot die functie later zelf gecalled wordt. Zonder closures zou partial application in JavaScript
vrij nutteloos zijn. Jammer genoeg ziet de syntax van de anonymous functions er ietwat
opgeblazen uit. Gelukkig is er in ECMAScript 6 (ES6) een nieuwe manier om anonymous
functions te schrijven, op een manier die meer op een echte lambda gelijkt.
var plus = a => b => a + b;
var plus2 = plus(2);
plus2(4) //6
Dit zijn arrow functions, waarbij alles voor de pijl argumenten zijn en alles erna de function
body. Indien er geen accolades zijn, wordt de function body automatisch gereturned.
Ook biedt JavaScript zelf ons ook enkele fundamentele functioneel georiënteerde functies aan
om in een wat meer functionele stijl te programmeren. De meeste populaire techniek om over
een array te itereren is de gekende for-loop.
let array = ['cola', 'fanta', 'sprite'];
for (var i = 0; i < array.length; i++) {
array[i] = array[i].toUpperCase());
}
Dit is duidelijk geïmplementeerd in een imperatieve manier. Er wordt veel code gebruikt om
uit te drukken hoe we bovenstaande array willen overlopen, veel code die grotendeels
20
irrelevant is in relatie tot ons doel. Tegelijkertijd zorgt een for-loop in dit geval ervoor dat er
veel meer ruimte is om fouten te maken. De functionele manier zou zijn om deze
functionaliteit te abstraheren om uiteindelijk een duidelijkere en meer leesbare implementatie
te hebben.
Vanaf ES5 biedt JavaScript dergelijke abstracties van de for-loop aan, abstracties die
fundamenteel zijn in functionele programmeertalen. Dit zijn higher order functions zoals map
(eerder al gezien in het eerste hoofdstuk), reduce en filter (forEach abstraheert ook een forloop maar is geen fundamentele functie in een functionele programmeertaal omdat het niet
resulteert in een nieuwe waarde en aanzet tot het gebruiken van side effects, maar het is wél
een higher order function).
Hoe zou bovenstaande code voorbeeld eruitzien wanneer we de map functie gebruiken?
const array = ['cola', 'fanta', 'sprite'];
const arrayMetHoofdLetters = array.map(soda => soda.toUpperCase());
Dit is korter en – nog belangrijker – het is declaratiever. Met andere woorden, deze code
drukt uit wat we willen doen, namelijk, pas de anonymous function soda =>
soda.toUpperCase() toe op alle elementen in de array en geef mij het resultaat hiervan terug.
Deze manier helpt ons ook om immutability toe te passen. De functie map muteert niet de
oorspronkelijke array, maar geeft in de plaats een volledige nieuwe array.
Een tweede abstractie die standaard te vinden is in JavaScript is filter, net zoals map een higher
order function. Zoals de naam waarschijnlijk al doet vermoeden, zal deze abstractie een array
filteren op basis van een gegeven functie die een boolean waarde teruggeeft.
const array = [{naam: 'John', leeftijd: 21},
{naam: 'Peter', leeftijd: 44},
{naam: 'Carl', leeftijd: 12}];
const ouderDan13 = array.filter(person => person.leeftijd > 13);
// [{naam: 'John', leeftijd: 21}, {naam: 'Peter', leeftijd: 44}]
Indien dit met een for-loop geïmplementeerd zou worden zou er logica geschreven moeten
worden om een nieuwe lijst aan te maken met personen ouder dan 13 of logica om personen
jonger dan 13 uit de oorspronkelijke array te verwijderen. Opnieuw, omdat deze logica al
geabstraheerd is in de filter functie is het duidelijker en declaratiever om het te gebruiken.
Waarschijnlijk de krachtigste abstractie aanwezig op het array prototype is reduce, ook wel
gekend als fold in vele functionele programmeertalen. Reduce is een functie die een array
21
neemt (in het geval van JavaScript neemt het niet letterlijk een array, het is eerder een
methode op Array.prototype) en die één enkele waarde teruggeeft – eender wat – op basis
van een functie die toegepast wordt op elk element uit deze array en de teruggegeven waarde
van de vorige loop, ook wel de accumulator genaamd. (Mozilla, 2016) Een simpel voorbeeld
hiervan is het optellen van alle nummers uit een array:
const array = [1, 2, 3, 4, 5];
array.reduce((vorigeWaarde, huidigeWaarde) => vorigeWaarde + huidigeWaarde);
// 15
Dit voorbeeld kan ervoor zorgen dat de lezer een verkeerd beeld krijgt van reduce, namelijk
dat het alle waarden in een array als het ware samenperst tot één waarde die van hetzelfde
type is van de elementen. Niets is minder waar. Reduce kan namelijk een extra parameter
nemen die de initiële waarde – of in het vorige code voorbeeld vorigeWaarde –
vertegenwoordigd. In volgend codevoorbeeld is de initiële waarde de index van een nog niet
gevonden waarde in een array, -1. Bij elke iteratie wordt gecontroleerd of de huidige waarde
onze gezochte waarde is of niet, en wordt er eventueel de index teruggegeven als de huidige
waarde daadwerkelijk onze gezochte waarde is. (UILab, 2016)
const indexVan = (zoek, array) => {
return arr.reduce((vorigeWaarde, huidigeWaarde, index) => {
return huidigeWaarde === zoek ? index : vorigeWaarde;
}, -1);
}
indexVan('druif', ['appel', 'banaan', 'sinaasappel']); // -1
indexVan('banaan', ['appel', 'banaan', 'sinaasappel']); // 1
Vrijwel alle methodes die beschikbaar zijn op het prototype van Array die standaard aanwezig
zijn in JavaScript kunnen met reduce herschreven worden, terwijl geen enkele van die
methodes reduce kan implementeren, zelfs niet map. Dit is mogelijk net omdat reduce éénder
wat kan teruggeven in plaats van simpelweg een nieuwe array. Stel dat we filter zouden
herschrijven met reduce, hoe zou dit eruit zien? (UILab, 2016)
const filter = (array, fn) => {
return array.reduce((vorigeWaarde, huidigeWaarde) =>
vorigeWaarde.concat(fn(huidigeWaarde) ? [huidigeWaarde] : []), []);
}
filter([2, 4, 8, 9], (n) => n % 2 === 0); // [2, 4, 8]
Het is duidelijk dat reduce een van de krachtigere functies is die JavaScript bezit om een array
te itereren. Het nemen van een array om per element iets te aggregeren tot een eindresultaat
22
is een zeer handig concept die in vele situaties kan toegepast worden waar het merendeel van
de programmeurs een for-loop zou gebruiken.
Arrays (en gelijkaardige data structuren) worden enorm veel gebruikt in code, JavaScript of
niet, en dus is de potentiële impact die het reguliere gebruiken van deze abstracties in code in
plaats van for-loops zeker niet te onderschatten. Er moet natuurlijk wel vermeld worden dat
for-loop’s nog altijd performanter zijn dan forEach, map, reduce etc. (JSPerf, 2016)
In een vorig hoofdstuk was er een vrij grote nadruk op composition, het samenvoegen van
meerdere, kleine, simpele en – het meest belangrijke – pure functies om een functie te
verkrijgen die een complex probleem kan oplossen. Vervolgens kunnen deze compositions
ook gebruikt worden in een andere composition en zo verder. Op deze manier kan eerder
welke gewenste functionaliteit geconstrueerd worden, en kan de flow van data doorheen deze
functies op een simpele manier gevolgd worden aangezien de volgorde expliciet wordt
uitgedrukt. Het aanwezig zijn van composition zorgt ervoor dat er meer herbruikbare code
geschreven wordt die kan gebruikt worden in meerdere verschillende function compositions,
wat op zijn beurt unit testen gemakkelijker maakt. Een voorbeeld hiervan:
var plus1 = a => a + 1;
var maal2 = b => b * 2;
var maal2plus1 = c => plus1(maal2(c));
maal2plus1(4); //9
In dit geval komt dit zeer simpel over, maar wanneer men abstracties gaat composen die een
duidelijke naam hebben kan dit een zeer declaratief overzicht van je code geven. Bijvoorbeeld,
er is een lijst die zelf enkele lijsten heeft van gegroepeerde taken die twee properties hebben,
namelijk de id en of de taak al vervolledigd is. Nu wordt er gevraagd om alleen maar de taken
te nemen die vervolledigd zijn en vervolgens deze te sorteren volgens de id. Dit kan als volgt
geïmplementeerd worden:
(Dit is momenteel nog geen correct JavaScript, aangezien de functies compose, filter, sorteer
en map niet standaard in JavaScript zitten. Deze code is puur ter illustratie om de elegantie
van compositie aan te tonen.)
var vervolledigdEnGesorteerd = compose(sorteer(taak => taak.id),
filter(taak => taak.vervolledigd == true));
map(vervolledigdEnGesorteerd, taken);
23
Compose(sorteer(…), filter(…)) hier is het equivalent van sorteer(taak => taak.id, filter(taak =>
taak.vervolledigd, [hier komt de variabele met taken]), compose zorgt ervoor dat de code in dit
geval wat leesbaarder is.
Merk ook op dat de functies sorteer en filter partieel geappliqueerd zijn. Wanneer deze
functies gecalled worden met enkel de functie die uitgevoerd zal worden voor elke taak –
ook wel het predicaat genoemd – zullen deze functies simpelweg een functie teruggeven die
taken verwacht. Dus wanneer de functie vervolledigdEnGesorteerd vervolgens gebruikt
wordt, verwacht deze taken. Die taken worden gebruikt in de filter functie die vervolgens
zijn resultaat – gefilterde taken – doorgeeft aan sorteer die op zijn beurt ook een functie is
die wacht op taken.
Uiteindelijk gebruiken we onze functie vervolledigdEnGesorteerd in een map functie – die
hetzelfde werkt als de gekende array.map() functie – die elke keer de composed functie
toepast op een lijst van gegroepeerde taken.
Indien dit probleem op een wat imperatievere manier was opgelost, met for-loops en
gemuteerde variabelen zou de code veel minder duidelijk en kort zijn als in dit geval. Hier
hebben we simpelweg – zoals al eerder beschreven is – enkele gekende abstracties
genomen, namelijk sorteer en filter, en deze gecomposed tot een nieuwe herbruikbare
functie, die op zijn beurt ook een abstractie is van onze probleemstelling: neem alleen de
vervolledigde taken en sorteer ze volgens id.
Omdat functionele programmeertalen geen equivalent van while of for hebben, worden alle
loops die niet over een lijst of dergelijke gaan uitgevoerd door recursie. Korte herhaling,
recursie is een concept waarbij een functie zichzelf oproept, en zo verder, tot er een
zogenaamde ‘edge case’ is bereikt - simpele if (predicaat) - die de recursie stopt. Recursie is
een van de bouwstenen van functioneel programmeren. In JavaScript daarentegen is het
werken met recursie stukken risicovoller. Als een bepaalde functie teveel recursief wordt
gecalled zal JavaScript ervoor zorgen dat de applicatie stopgezet wordt, aangezien er teveel
onafgewerkte functies op de call stack.
Dit wil niet zeggen dat recursie niet gebruikt kan worden in JavaScript. ES6 heeft er namelijk
voor gezorgd dat tail call recursie mogelijk is zonder dat de call stack gevuld wordt met nog
niet afgelopen functies. Met tail call wordt er bedoelt dat wanneer een functie zichzelf
oproept, deze statement de laatste is in de functie en gereturned wordt. Bijvoorbeeld:
var telAfNaar0 = getal => {
if (getal < 0) return;
console.log(getal);
return telAfNaar0(getal - 1);
}
telAfNaar0(5); // 5 4 3 2 1 0
24
In ES5 zou de call stack gevuld zijn met 5 calls naar telAfNaar0, in ES6 wordt dit
geoptimaliseerd en is er bij iedere recursie maar 1 call op de stack, namelijk de huidige. Maar
waarom gebruik maken van recursie in de eerste plaats? Alles dat met een for statement
geschreven is kan ook met recursie gecodeerd worden, maar, niet alles dat recursief
geprogrammeerd is kan daadwerkelijk met een for loop geschreven worden. Stel – dit is vrij
abstract uitgelegd – er is een object dat een dynamisch aantal geneste objecten heeft –
conceptueel gelijkaardig aan een tree diagram of een DOM-structuur bijvoorbeeld – en het is
nodig om elk individueel object te onderzoeken. Dit is vrijwel onmogelijk met for-loops omdat
het ongekend is hoe diep een object genest kan zijn. (Johansson, Recursion - Part 7 of
Functional Programming in JavaScript, 2015)
Maar dit soort gecompliceerd probleem komt niet vaak voor in het dagdagelijkse leven van
een developer. Los hiervan, recursie is een elegantere manier om een loop uit te drukken,
omdat het – dit is al meerdere keer aangehaald in deze scriptie – duidelijk maakt wat er moet
gebeuren in plaats van hoe er iets moet gebeuren. Dit kan opnieuw duidelijk gemaakt worden
door telAfNaar0 imperatief te implementeren (in dit geval is er maar één for loop, maar
wanneer er meerdere geneste for loops zijn, kan dit een stuk oneleganter zijn dan recursie):
var telAfNaar0 = getal => {
for (var i = getal; i >= 0; i--) {
console.log(i);
}
}
Lazy evaluation was een van de fundamentele features van - het merendeel van – de
functionele programmeertalen. Standaard is dit niet aanwezig in JavaScript. Dit heeft als
nadeel dat wanneer er bijvoorbeeld met een zeer grote array gewerkt wordt en wordt een vrij
lange chain van operaties op die array toegepast (meerdere array methodes zoals map, filter,
reduce etc.), dan zal bij elke stap van de chain de gehele array overlopen worden, terwijl op
het einde van de chain misschien maar het eerste element uit het resultaat vereist is. Er zijn
enkele externe libraries die dit probleem verhelpen, maar meer daarover in het volgende
hoofdstuk.
In het vorige hoofdstuk is er aangetoond dat standaard JavaScript zich leent tot het toepassen
van concepten uit het functioneel programmeren, maar in zekere zin is het nog vrij laks. Als
gevolg van de globale populariteit van JavaScript is er een enorme open source cultuur, met
alsmaar nieuwe frameworks, libraries, tools etc. die inspiratie nemen uit bestaande talen of
dergelijke. Het is dan ook niet toevallig dat de recente populariteit van een functionele
programmeerstijl in JavaScript grotendeels toe te wijden is aan enkele van deze nieuwe open
source projecten die dergelijke programmeerstijl promoten, gepaard met de – al dan niet
verdiende – hype die deze recente projecten genieten, een eigenschap eigen aan het
25
gigantische JavaScript open source ecosysteem. Hier volgt een selectie van dergelijke
projecten.
A programmer armed with a repertoire of fundamental functions and, more
importantly, the knowledge on how to use them, is much more effective than one who
starts from scratch. Unfortunately, a standard JavaScript environment comes with
deplorably few essential functions, so we have to write them ourselves or, which is
often preferable, make use of somebody else’s code. (Haverbeke, 2011)
Libraries die dergelijke essentiële functies ter beschikking stellen zijn ook wel gekend als
utility libraries. De meest bekende hiervan zijn Underscore en Lodash. Deze bieden handige
functies aan om over objecten, arrays en strings te itereren en om eenvoudiger met objecten
te kunnen werken. Ook zijn er meerdere handige predicaatfuncties, zoals bijvoorbeeld
isUndefined() i.p.v. een if statement (handig voor composition), meerdere utility functions
voor functies zelf en nog veel meer.
Ondanks wat de documentatie van Underscore vertelt, is het geen volledig functioneel
programmeergerichte utility library, omdat in tegenstelling tot de andere twee alternatieven
– Lodash en Ramda - de helper functies niet automatisch gecurried worden (vergelijkbaar met
partieel appliqueren, een gecurriede functie geeft altijd een nieuwe functie terug tot het al
zijn argumenten verkregen heeft) en is de data niet het laatste argument bij functies. Er moet
opgemerkt worden dat Lodash op zich ook niet deze eigenschappen bezit, maar het stelt een
aparte versie ter beschikking – Lodash/FP – dat deze eigenschappen wel beschikt. Om deze
verschillen te illustreren volgt een vergelijking tussen Underscore en Lodash/FP. (Lonsdorf,
Hey Underscore, You're Doing It Wrong!, 2013)
//Underscore
var eersteTweeLetters = function (woorden) {
return _.map(words, function (woord) {
return _.first(woord, 2);
});
};
//Lodash/fp
var eersteTweeLetters = _.map(_.take(2));
eersteTweeLetters(['mike', 'sara']); // ['mi', 'sa'];
(_.first() uit Underscore en _.take() uit Lodash hebben in deze situatie dezelfde functionaliteit,
ze nemen een array – of in dit geval een string – en nemen een getal dat aanduidt hoeveel
elementen van de array het teruggeeft.
Hier is het Lodash/fp voorbeeld een stuk korter, omdat het – volgens de documentatie –
“Immutable auto-curried iteratee-first data-last methods” zijn. (Lodash, 2016) Argumenten
26
die later gegeven zullen worden, worden niet vermeld omdat _.map() en _.take() beide al
partieel geappliqueerd zijn en simpelweg functies zijn die de nodige data verwachten. Deze
stijl van programmeren is ook wel gekend als ‘point-free programming’, een onderdeel van
functioneel programmeren, waarbij functies de data argumenten waarmee ze werken niet
vermelden. (Wikipedia, 2016)
Deze stijl van programmeren heeft ook nadelen, namelijk dat het kan leiden tot enige
obfuscatie. Het kan moeilijk zijn om de intentie van een functie af te leiden indien deze functie
ongekend is door de developer. Daarom is het soms beter om met gewone functies te werken
wanneer nodig. (Lonsdorf, Professor Frisby's Mostly Adequate Guide To Functional
Programming, 2016)
Verder heeft Lodash/FP enkele functies die de developer helpen om op een meer functionele
manier te schrijven. Een van deze functies is al eerder de revue gepasseerd in het vorige
hoofdstuk – maar waarvan er toen gezegd was dat het geen werkende standaard JavaScript
code was – namelijk compose. Wat compose juist is, is al uitgelegd. Currying en composition
gaan hand in hand wanneer er in een functionele stijl geprogrammeerd wil worden en zorgen
ervoor dat er beknopte statements geschreven kunnen worden die eruitzien alsof er data
doorheen een compose functie gesluisd wordt.
//onbestaande functies
var pasAndereAbstractieToe = _.compose(neemEnkeleElementen, sorteerIets)
var pasAbstractieToe = _.compose(reduceIets, pasAndereAbstractieToe);
var resultaat = _.compose(pasAbstractieToe, mapIets, filterIets);
Dit in tegenstelling tot de manier die Underscore aanraadt om meerdere abstracties in één
statement uit te voeren, namelijk met de _.chain() functie die een data argument neemt en
deze vervolgens verpakt of ‘wrapped’ in een object waardoor er continu een ‘.’ kan gebruikt
worden om als het ware statements te chainen. _.compose() op zich is meer functioneel – en
de duidelijke keuze bij het programmeren in een functionele stijl – en is meestal iets
beknopter maar uiteindelijk zijn beide _.compose() en _.chain() een manier om meer leesbare
code te schrijven in JavaScript, kennelijk dienen ze hetzelfde doel. (Lonsdorf, Hey
Underscore, You're Doing It Wrong!, 2013)
Om te illustreren dat het gebruiken van abstracties uit deze utility libraries en het gebruiken
van curried functies code meer expressiever en leesbaarder maken volgt hier een
codevoorbeeld dat een typisch scenario uitbeeldt, een scenario dat elke developer
waarschijnlijk ooit wel in een of andere vorm zal aantreffen. Deze code komt uit een blogpost
geschreven door één van de makers van Ramda. (Sauyet, 2016) Ramda is een library die nog
een stapje extra neemt in de richting van functioneel programmeren. Het is momenteel nog
niet aangeraden om het te gebruiken in productie omdat het nog geen 1.0 versie heeft. Dit wil
zeggen dat de library vaak drastische aanpassingen maakt die functionaliteit breekt.
Stel, er is een request naar een REST API die enkele taken teruggeeft voor een specifieke user.
27
var data = {
tasks: [
{id: 104, complete: false,
dueDate: "2013-11-29",
title: "Do something",
{id: 105, complete: false,
dueDate: "2013-11-22",
title: "Do something else",
{id: 107, complete: true,
dueDate: "2013-11-22",
title: "Fix the foo",
]
};
priority: "high",
username: "Scott",
created: "9/22/2013"},
priority: "medium",
username: "Lena",
created: "9/22/2013"},
priority: "high",
username: "Mike",
created: "9/22/2013"}
Er is een functie nodig die alle onvoltooide taken neemt, daarvan alleen maar enkele nodige
properties neemt en die dit vervolgens sorteert op de deadline. De volgende code is al in een
functionele stijl geschreven, maar kan het nog beknopter en leesbaarder?
var getIncompleteTaskSummaries = function (membername) {
return fetchData().then(function(data) {
return data.tasks.filter(function(t){
return t.username == membername && !t.complete;
}).map(function(t){
var copy={}, props=["id","dueDate","title","priority"], p;
while(p=props.pop()){ copy[p] = t[p] }
return copy;
}).sort(function(first, second) {
return first.dueDate - second.dueDate;
});
});
};
Indien dit probleem opgelost zou worden door een beginnende programmeur zou het nog
langer, onduidelijker en imperatiever zijn. Dit probleem geïmplementeerd met utility
functions uit de Ramda library – die gelijkaardig met Lodash/FP automatisch gecurried zijn –
zou er als volgt uitzien:
var getIncompleteTaskSummaries = function (membername) {
return fetchData()
.then(R.get('tasks'))
.then(R.filter(R.propEq('username', membername)))
.then(R.reject(R.propEq('complete', true)))
.then(R.map(R.pick(['id', 'dueDate', 'title', 'priority'])))
.then(R.sortBy(R.get('dueDate')));
};
28
Qua lengte is er in dit geval een miniem verschil, maar volgens de schrijver van de blogpost
is dit veel expressiever en duidelijker. Hoe complexer het probleem wordt, hoe handiger het
wordt om het op te splitsen in deelproblemen die beknopter – met minder boilerplate die niet
relevant zijn aan het probleem – kunnen uitgedrukt worden a.d.h.v. dergelijke utility
functions en functies zoals _.compose(). Wie weet moet er een maand nadat een bestaande app
gereleaset is een nieuwe functionaliteit toegevoegd worden die verder bouwt op bestaande
functionaliteit. Het reeds hebben van meerdere herbruikbare abstracties – die soms dataagnostisch zijn – kan van pas komen om de gerelateerde nieuwe functionaliteit snel toe te
voegen.
Er moet opgemerkt worden dat wanneer bovenstaand probleem opgelost wordt in ES6 samen
met een library zoals Underscore of Lodash, dat het ook compact en leesbaar geïmplementeerd
kan worden met behulp van de nieuwe arrow functies. Het lijkt misschien wat minder op
traditionele functionele programmeertalen als Haskell, maar opnieuw, beide manieren
streven éénzelfde doel na, duidelijke code schrijven. De keuze ligt aan de developer op welke
manier deze het liefst leesbare code schrijft – ‘point-free’ of klassiek met ES6 – zolang het
doel blijft om op een beknopte en expressieve wijze problemen op te lossen, en hierbij kunnen
utility libraries zoals Lodash met zeer veel handige abstracties een enorme hulp zijn,
functionele versie of niet.
Eerder werd er ook vermeld dat standaard JavaScript niet beschikt over lazy evaluation.
Lodash & Ramda beschikken in zekere zin over lazy evaluation met enkele methodes, en in
het geval van Lodash wordt lazy evaluation maar toegepast eens er gewerkt wordt met arrays
of dergelijke met meer dan 200 elementen. Een andere library die zich volledig focust op lazy
evaluation en die gelijkaardige utility functions heeft als Underscore of Lodash is Lazy.js.
(LazyJS, 2016)
React is – ondanks wat velen denken – geen framework zoals AngularJS er een is. Het is
simpelweg een library die jou een manier geeft om ‘componenten’ te maken in een template
taal (JSX) die HTML renderen en waarvoor enkele lifecycle functies of dergelijke worden
aangeboden. Het wordt ook wel gezien als de V (view) in MVC en dus is het vanzelfsprekend
niet genoeg om een gelijkaardige applicatie met alleen maar React te maken zoals dit zou
kunnen gemaakt worden met een framework zoals AngularJS of Ember.
Voor een wat diepere kijk op React kan de lezer terecht op de officiële pagina. (React, 2016)
Deze library wordt hier aangehaald omdat het de eerste enorm populaire library is die
concepten uit het functioneel programmeren nadrukkelijk promoot. Vanuit een zeer abstract
opzicht is het werken met React components al een vorm van composition.
<Menu>
<MenuItem>Product</MenuItem>
<MenuItem>Over Ons</MenuItem>
</Menu>
29
Componenten in JSX (bijvoorbeeld <Menu>) kunnen ook gezien worden als functies die
componenten creëren aan de hand van verkregen argumenten ook wel gekend als props in
het React ecosysteem. Hier kan het concept van pure functies perfect gebruikt worden. Geef
dezelfde props aan een component en de developer kan er 100% zeker van zijn dat altijd
hetzelfde resultaat gerendered zal worden. Het merendeel van de componenten in een
volledige applicatie kunnen geschreven worden als een pure functie en om deze reden heeft
React recent ook een nieuwe syntax geïntroduceerd die ervoor zorgt dat de developer een
React component letterlijk als een functie kan schrijven.
const Text = ({ content }) => <p>{content}</p>;
Buiten het feit dat dit soort componenten React-agnostisch zijn en dus gebruikt kunnen
worden als eender elke andere functie zijn er nog enkele extra voordelen. Als men het gebruik
van deze soort componenten kan maximaliseren is het vanzelfsprekend dat het gemakkelijker
wordt om te redeneren over de werking en data flow van een React applicatie. Een tweede
voordeel, gegeven door het React team in een blogpost over een nieuwe release van React
gaat als volgt:
This pattern is designed to encourage the creation of these simple components that
should comprise large portions of your apps. In the future, we’ll also be able to make
performance optimizations specific to these components by avoiding unnecessary
checks and memory allocations. (React, 2015)
Zoals eerder vermeld is, is React enkel maar een UI library, dus kan onder andere het omgaan
met state in je applicatie geïmplementeerd worden met een andere library. Een van deze
libraries is Redux, gebaseerd op het patroon Flux dat oorspronkelijk door Facebook
gelijklopend met React is geïntroduceerd om de data flow uni-directioneel te organiseren
(voor meer informatie kan de lezer terecht op de documentatie van Flux (Flux, 2016)). Redux
zorgt ervoor dat alle state voor componenten in één object zit in een zogenaamde store, ook
wel gekend als de ‘single source of truth’. De enige manier om de state aan te passen is als er
ergens een action wordt ge-emit. Een action beschrijft wat er gebeurd is, bijvoorbeeld
NEW_MESSAGE, en kan eventueel nieuwe data met zich meegeven. Deze actions worden
verwerkt door reducers (zie vorig hoofdstuk), functies die a.d.h.v. een action de huidige state
uit de store nemen en deze reducen tot de nieuwe state met optionele data meegegeven met
de action. Vervolgens wordt het state object in de store vervangen door deze nieuwe state.
(Redux, 2016)
Deze library is duidelijk gebaseerd op enkele functionele concepten. Een eerste concept is
immutability. Elke keer een action wordt ge-emit wordt de oorspronkelijke state niet
gemuteerd, maar wordt eerder volledig opnieuw aangemaakt als er aanpassingen zijn. Dit
zorgt ervoor dat de state als ‘read-only’ gezien kan worden, buiten het emitten van actions.
Op deze manier is er een duidelijk overzicht over mogelijke veranderingen in de state en over
de strikte volgorde van deze actions. Dit gaat hand in hand met een tweede concept, namelijk
dat reducers pure functies zijn die ervoor zorgen dat aanpassingen aan de state zeer
30
voorspelbaar zijn. Dit in vergelijking met de huidige stand van zaken waar state bij front end
applicaties vaak niet betrouwbaar is omdat bijvoorbeeld een bepaald model een view kan
updaten die op zijn beurt een ander model updatet. Dit kan zeer verwarrend zijn, een
probleem dat Redux probeert te verhelpen, door alle state op één plaats te hebben waarop
gecontroleerd aanpassingen gemaakt kunnen worden. (Redux, 2016)
Vervolgens zijn deze actions ook simpele JavaScript objecten, waardoor deze kunnen
opgeslagen worden om later eventueel opnieuw uit te voeren, bij het debuggen of dergelijke.
(Redux, 2016)
Een van de fundamentele concepten uit het functioneel programmeren die in deze scriptie
nog niet besproken is met betrekking tot JavaScript is immutability. Immutability was één
van de redenen gegeven waarom programma’s geschreven in een pure functionele
programmeertaal stabieler zijn, waarom het eenvoudiger is om over de werking van de code
te redeneren (bijvoorbeeld niet nodig om mentaal doorheen een programma te stappen —
gelijkaardig aan een debugger — om over de huidige state van een object te redeneren) en
waarom er minder potentiële bugs zijn in een dergelijk programma met grotendeels
onveranderlijke datastructuren.
Verder zorgt immutability er ook grotendeels voor dat het programmeren met meerdere
threads — concurrent programming — veel eenvoudiger wordt vergeleken met concurrency
bij bijvoorbeeld Java, waar waardes vanop verschillende threads gemuteerd kunnen worden.
Dit voordeel echter kan niet toegepast worden in het JavaScript ecosysteem omdat JavaScript
single-threaded is, maar het houdt de developer niet tegen om immutability te gebruiken voor
enkele andere voordelen.
Nu zijn er standaard geen onveranderlijke datastructuren in JavaScript, zelfs niet const (const
is geen onveranderlijke waarde, maar eerder een onveranderlijke referentie naar een waarde),
maar er zijn wel enkele libraries die onveranderlijke datastructuren ter beschikking stellen.
Een van deze libraries is ImmutableJS, grotendeels geschreven door Lee Byron. Het heeft
datastructuren zoals Map, List, Set, Stack, etc. en heeft van nature lazy evaluation (Seq).
(Facebook, 2016)
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
Een library zoals ImmutableJS is vooral handig bij recente libraries zoals React of dergelijke
die de developer toelaten om te kiezen om een component maar opnieuw renderen wanneer
deze nieuwe props of state krijgt. Indien dit geïmplementeerd zou zijn met standaard
JavaScript objecten zou een ‘deep comparison’ nodig zijn, waarbij heel de structuur van een
object overlopen moet worden. Aangezien een component zeer vaak zal kijken of het moet
31
opnieuw renderen is dit niet performant. Wanneer er onveranderlijke datastructuren gebruikt
worden uit bijvoorbeeld ImmutableJS is dergelijke ‘deep comparison’ niet nodig. Er kan
simpelweg gebruik gemaakt worden van een ===. Als de reference van de twee
onveranderlijke datastructuren verschillen kunnen we zeker zijn dat er iets veranderd is en
omgekeerd. Dit is een stuk performanter en kan in sommige gevallen zorgen voor een
drastisch kleiner aantal keren dat components opnieuw gerendered moeten worden.
Jammer genoeg is de API van ImmutableJS niet hetzelfde als deze van standaard JavaScript
datastructuren en dus kan het in een grotere codebase soms onduidelijk zijn of een bepaald
argument ergens een ImmutableJS datastructuur is of eerder een standaard JavaScript
datastructuur is. (Longster, 2015)
var neemEigenschap = function(map, eigenschap) {
return map.get(eigenschap);
}
neemEigenschap({name: 'Tom'}, 'name'); // Error: map.get is not a function
Zoals al eerder aangehaald is in het begin van hoofdstuk 3 is JavaScript op zich geen
volwaardige functionele programmeertaal zoals Haskell of Clojure er zijn. Het mist een aantal
fundamentele concepten die in vrijwel alle functionele talen aanwezig zijn, zowel met
JavaScript als met externe libraries. Dit zijn features als ‘pattern matching’, algebraïsche
datatypes, etc. Verder is JavaScript ook niet geschreven als een functionele programmeertaal,
maar is het eerder een taal die meerdere programmeerparadigma’s ondersteunt, zoals
functioneel en imperatief. Dit soort talen zijn ook wel gekend als multiparadigmaprogrammeertalen. (Wikipedia , 2016)
Er zijn nog enkele concepten uit het functioneel programmeren die in deze bachelorproef niet
aan bod zijn gekomen, mede door een tekort aan woorden, maar grotendeels omdat ze buiten
de complexiteit van deze bachelorproef vallen. Concepten zoals ‘Monads’, ‘Functors’, etc.
Net omwille van het feit dat JavaScript een multi-paradigmaprogrammeertaal is, is het niet
onmiddellijk aan te raden om volledig functioneel te programmeren in JavaScript, vooral als
de developer nog geen eerdere ervaring heeft met functioneel programmeren. Dit gaat samen
met de vele mogelijke side-effects die van nature aanwezig zijn bij web development, zoals
DOM operaties, AJAX calls, etc.
Maar niks weerhoudt een developer om – waar toepasselijk – concepten uit het functioneel
programmeren toe te passen, concepten die in de loop van deze bachelorproef toegelicht zijn.
Er werd bij elk concept duidelijk gemaakt wat de voordelen ervan zijn, voordelen die allemaal
een gemeenschappelijk doel hebben: kortere, declaratievere, stabielere en meer voorspelbare
code schrijven.
32
Het gehele idee van code schrijven in een functionele programmeerstijl in JavaScript kan vrij
kort samengevat worden. Ten eerste kan er geprobeerd worden om zoveel mogelijk pure
functies te schrijven, maar waar nodig – bijvoorbeeld AJAX calls – kan er natuurlijk een
functie geschreven worden die side-effects heeft. Tegelijkertijd is het aangeraden om zo
weinig mogelijk te vertrouwen op ongecontroleerde globale state – in tegenstelling tot Redux
- maar opnieuw, wanneer absoluut nodig moet de developer niet terughoudend zijn om deze
te muteren. Meer pure functies en minder globale state zorgt voor een duidelijker overzicht
van de data flow doorheen een programma en heeft als extra gevolg dat er meer en simpelere
unit testen geschreven kunnen worden. En ten tweede is het de bedoeling om met – al dan
niet zelfgeschreven – abstracties op een zo high-level mogelijke manier programma’s
proberen te schrijven, waar de nadruk van de code ligt op wat de probleemstelling van het
programma is, en niet op hoe het probleem stap voor stap opgelost wordt.
Bovenstaande zijn dan ook maar richtlijnen en geen verplichtingen. Indien voor een bepaald
probleem een OOP-stijl het beste past, heeft het natuurlijk geen nut om toch zichzelf te
forceren om in een functionele stijl te schreven. Dit benadrukt vervolgens ook de
veelzijdigheid van JavaScript. Afhankelijk van de situatie kan in een verschillende
programmeerstijl geschreven worden, een programmeerstijl die zich er het best toe leent om
een probleem op te lossen.
Hier kan opnieuw ook hetzelfde argument aangehaald worden als in het eerste hoofdstuk om
te verklaren waarom je als developer juist niet in een functionele stijl zou programmeren,
namelijk het feit dat deze stijl vaak ietwat minder performant is dan imperatief JavaScript. Dit
kan belangrijk zijn bij web applicaties die als grootste prioriteit performantie hebben
(bijvoorbeeld online games).
33
In conclusie, de recente populariteit van concepten uit het functioneel programmeren bij web
development is meer dan een recente gril omdat het wel degelijk het leven van een developer
eenvoudiger maakt wanneer deze aan een moderne web applicaties werkt die almaar
complexer worden. De instapdrempel tot het gebruiken van simpele functionele concepten is
vrij laag bij JavaScript vergeleken met volwaardige functionele programmeertalen, en
geleidelijk aan kan de developer extra concepten beginnen te gebruiken naarmate zijn kennis
omtrent deze functionele concepten toeneemt. Vervolgens wordt deze transitie naar een meer
functionele stijl van programmeren niet alleen bekrachtigd door alom bekende libraries zoals
React, maar – belangrijker nog – ook door Ecma – de onderhouders van ECMAScript – zelf.
Het is aangeraden om waar toepasselijk een functionele stijl te gebruiken.
34
Allen, C., & Moronuki, J. (2015). Haskell Programming from first principles.
Delft University of Technology. (2014, September 4). Programming in Haskell chapter 11
Lazy Evaluation. Opgehaald van
http://ocw.tudelft.nl/courses/computerscience/introduction-to-functionalprogramming/lazy-evaluation/
Duchene, A. (2014, April 4). What are some limitations/disadvantages of functional
programming? Opgehaald van Quora: https://www.quora.com/What-are-somelimitations-disadvantages-of-functional-programming/answer/Alexander-Duchene
Eidhof, C. (2008, November 12). Vraag over lazy evaluation. Opgehaald van Stackoverflow:
http://stackoverflow.com/a/284180/1243641
Erlang. (2016, Februari 25). Info over Higher Order Functions bij Erlang. Opgehaald van
Website van Higher Order Functions bij Erlang:
http://erlang.org/documentation/doc-4.8.2/doc/extensions/funs.html
Facebook. (2016, Maart 21). ImmutableJS. Opgehaald van ImmutableJS:
https://facebook.github.io/immutable-js/%5D
Flux. (2016, Maart 22). Flux documentation overview. Opgehaald van Flux:
https://facebook.github.io/flux/docs/overview.html
Ford, N. (2013, Januari 29). Functional thinking: Why functional programming is on the rise.
Opgehaald van IBM developerWorks:
http://www.ibm.com/developerworks/library/j-ft20/
Haskell Wiki. (2016, Maart 8). Why Haskell Matters. Opgehaald van Haskell Wiki:
https://wiki.haskell.org/Why_Haskell_matters
Haverbeke, M. (2011, Februari 6). Chapter 6 Functional Programming. Opgehaald van
Eloquent JavaScript: http://eloquentjavascript.net/1st_edition/chapter6.html
Hickey, R. (2011, October 13). Simple Made Easy. Opgehaald van InfoQ:
http://www.infoq.com/presentations/Simple-Made-Easy
Hudak, P. (1989). Conception, Evolution, and Application of Functional Programming
Languages. ACM Computing Surveys, 361-391.
Hughes, J. (1984). Why Functional Programming Matters. 22. Opgehaald van Chalmers:
http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf
Jelvis, T. (2014, April 1). What are some limitations/disadvantages of functional programming?
Opgehaald van Quora: https://www.quora.com/What-are-some-limitationsdisadvantages-of-functional-programming/answer/Tikhon-Jelvis
Johansson, M. P. (2015, Augustus 24). Recursion - Part 7 of Functional Programming in
JavaScript. Opgehaald van YouTube: https://www.youtube.com/watch?v=k7-N8R0KY4
Johansson, M. P. (2015, Augustus 24). Recursion - Part 7 of Functional Programming in
JavaScript. Opgehaald van YouTube: https://www.youtube.com/watch?v=k7-N8R0KY4
JSPerf. (2016, Maart 15). for vs forEach. Opgehaald van JSPerf: https://jsperf.com/for-vsforeach/494
LazyJS. (2016, Maart 21). LazyJS. Opgehaald van LazyJS: http://danieltao.com/lazy.js/
Lipovača, M. (2016, Februari 25). Info over Learn You A Haskell. Opgehaald van Website van
Learn You A Haskell: http://learnyouahaskell.com/higher-order-functions
35
Lodash. (2016, Maart 20). FP Guide. Opgehaald van GitHub Lodash Wiki:
https://github.com/lodash/lodash/wiki/FP-Guide#mapping
Longster, J. (2015, Oktober 1). Using Immutable Data Structures in JavaScript. Opgehaald van
jlongster: http://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript
Lonsdorf, B. (2013, Mei 21). Hey Underscore, You're Doing It Wrong! Opgehaald van YouTube:
https://www.youtube.com/watch?v=m3svKOdZijA&app=desktop
Lonsdorf, B. (2016, Maart 20). Professor Frisby's Mostly Adequate Guide To Functional
Programming. Opgehaald van GitBook: https://drboolean.gitbooks.io/mostlyadequate-guide/content/ch5.html
Mozilla. (2016, Maart 03). Array.prototype.reduce(). Opgehaald van Mozilla Development
Network: https://developer.mozilla.org/enUS/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
React. (2015, September 10). React Blog. Opgehaald van React:
https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#statelessfunction-components
React. (2016, Maart 24). Advanced Performance. Opgehaald van React:
https://facebook.github.io/react/docs/advanced-performance.html
React. (2016, Maart 22). React: Getting started. Opgehaald van React:
https://facebook.github.io/react/docs/getting-started.html
Redux. (2016, Maart 22). Redux. Opgehaald van Redux: http://redux.js.org/
Redux. (2016, Maart 22). Redux motivation. Opgehaald van Redux:
http://redux.js.org/docs/introduction/Motivation.html
Redux. (2016, Maart 22). Redux: Three Principles. Opgehaald van Redux:
http://redux.js.org/docs/introduction/ThreePrinciples.html
Sauyet, S. (2016, Maart 21). Favoring Curry. Opgehaald van Frumio:
http://fr.umio.us/favoring-curry/
UILab. (2016, Maart 16). Array Reduce Like You (may) Have Never Seen It. Opgehaald van
UILab: http://www.uilab.io/posts/array-reduce-like-you-may-have-never-seen-it
University of Pennsylvania. (2014, Oktober 16). Being Lazy with Class. Opgehaald van CIS
194: Introduction to Haskell (Spring 2015):
http://www.seas.upenn.edu/~cis194/lectures/06-laziness.html
Wiki.Haskell. (2016, Februari 25). Info over Partial Application. Opgehaald van Website van
Haskell Wiki: https://wiki.haskell.org/Partial_application
Wikipedia . (2016, Maart 13). JavaScript. Opgehaald van Wikipedia:
https://en.wikipedia.org/wiki/JavaScript
Wikipedia. (2016, Februari 12). Info over Functioneel Programmeren. Opgehaald van Website
van Wikipedia: https://en.wikipedia.org/wiki/Functional_programming
Wikipedia. (2016, Februari 12). Info over Imperatief Programmeren. Opgehaald van Website
van Wikipedia: https://en.wikipedia.org/wiki/Imperative_programming
Wikipedia. (2016, Maart 6). Info over Lazy Evaluation. Opgehaald van Website van
Wikipedia: https://en.wikipedia.org/wiki/Lazy_evaluation
Wikipedia. (2016, Maart 20). Tacit Programming. Opgehaald van Wikipedia:
https://en.wikipedia.org/wiki/Tacit_programming
36
Download