Als je vanuit het niets binnenkomt op de tweede plek in het lijstje van favoriete programmeertalen, dan doe je iets goed. Het gebeurde Kotlin onlangs; jaarlijks publiceert Stack Overflow de resultaten van een onderzoek naar de favoriete programmeertalen onder ontwikkelaars, studenten en onderzoekers. Kotlin was daarbij de hoogste nieuwe binnenkomer, op nummer twee. De nummer één is, net als vorig jaar, Rust.
Wat vooral opvalt, is dat het functionele paradigma in opmars is in ontwikkelaarsland. Zowel de nummers één als twee in de lijst (Rust en Kotlin) hebben veel functionele aspecten en dat geldt ook voor andere talen die hoog eindigen, zoals Scala en Go.
Dat is een opvallende verandering, omdat al sinds de jaren negentig met name objectgeoriënteerde talen in zwang zijn – en daar lijkt nu dus verandering in te komen. Niet alleen stijgt de populariteit van functionele programmeertalen, ook zie je dat objectgeoriënteerde talen zoals C#, Java en C++ steeds meer functionele elementen in zich krijgen.
Functioneel versus objectgeoriënteerd
Voordat we op zoek gaan naar een verklaring hiervoor, is het goed om eerst eens te kijken naar het verschil tussen de twee talenfamilies. Objectoriëntatie is gebaseerd op het idee dat je in een programma een model van de werkelijkheid probeert te creëren. Net zoals je in de werkelijkheid objecten herkent (zoals een auto of een stoel), maken objectgeoriënteerde programmeertalen ook gebruik van abstracte objecten, zoals ‘bedrijf’ of ‘contract’. In deze benadering probeer je de code zo dicht mogelijk bij de werkelijkheid te laten aansluiten.
Functionele programmeertalen hebben een heel ander uitgangspunt. Deze talen zijn gebaseerd op het principe van het wiskundige functiebegrip. Er is een functie, daar stop je input in en je krijgt er een output uit (een returnwaarde).
Hoe werkt dat in de praktijk? Neem bijvoorbeeld een hr-applicatie. In een objectgeoriënteerde aanpak werk je dan bijvoorbeeld met een collectie met werknemers (de objecten). Als deze werknemers een salarisverhoging krijgen, pas je, een voor een, de bijbehorende objecten aan. Als deze hr-applicatie in een functionele taal is geschreven, dan zul je waarschijnlijk te maken hebben met een lijst met werknemers. Als deze werknemers een salarisverhoging krijgen, dan beschrijf je door middel van een functie hoe het resultaat eruit moet zien: een lijst met werknemers, waarvan het salaris hoger is.
Dat is dus een wezenlijk andere benadering van programmeren. Maar hoe komt het dat deze manier van programmeren terrein lijkt te winnen?
Immutability
Een van de belangrijkste eigenschappen van functionele programmeertalen – en de reden waarom ze zo populair zijn – is dat ze worden gekenmerkt door een concept dat immutability heet.
Immutability wil zeggen dat een voorwerp onveranderlijk is; als je het eenmaal hebt gecreëerd, dan kun je het niet meer aanpassen. Dit is een stuk eenvoudiger toe te passen binnen functionele talen dan binnen objectgeoriënteerde talen. Datastructuren zijn binnen functionele talen onveranderbaar (immutable dus); er wordt niet gewerkt met variabelen, in plaats daarvan werk je met waarden die worden meegegeven aan een bepaalde functie.
Dat is een wezenlijk verschil met de objectgeoriënteerde aanpak. Als binnen een objectgeoriënteerd model een object is aangemaakt als beeld van de werkelijkheid (de werknemer in het voorbeeld) en de werkelijkheid verandert (een hoger salaris), dan is het gevolg dat het object ook verandert. En omdat de werkelijkheid nu eenmaal de neiging heeft om voortdurend te veranderen, veranderen de objecten in een objectgeoriënteerd model ook steeds.
Bij functionele talen staan niet objecten centraal, maar parameters en die worden bij elke aanroep opnieuw meegegeven. Immutability is daardoor eenvoudiger binnen functionele talen dan in objectgeoriënteerde talen.
Immutability is de afgelopen jaren een belangrijkere eigenschap geworden van software, omdat organisaties applicaties steeds vaker verspreiden over verschillende cores of processoren – hetzij fysiek, of in de cloud. Dit zogeheten parallelliseren van software is veel eenvoudiger te realiseren als een applicatie is geschreven in een functionele programmeertaal.
Daar komt nog bij dat functionele talen meer declaratieve aspecten hebben dan objectgeoriënteerde talen, wat het ook weer eenvoudiger maakt om applicaties te parallelliseren.
Schaalbaarheid
Nu zijn functionele talen niet nieuw; in de wetenschappelijke wereld zijn deze allang populair. Maar de recente opmars laat zien dat deze ‘taalgroep’ een streepje voor lijkt te krijgen op de objectgeoriënteerde talen waar de meeste developers nu nog gewend zijn om mee te werken. Daar komt bij dat in objectgeoriënteerde talen zoals C#, Java en C++ steeds meer functionele aspecten sluipen.
Deze opmars is dus vooral te verklaren door de groeiende behoefte binnen it om applicaties schaalbaar te maken en dus over meerdere processoren te verdelen.
Aanpassing
Voor veel developers die gewend zijn om op een objectgeoriënteerde manier te werken, betekent dit een hele omslag. Ook als ze gespecialiseerd blijven in objectgeoriënteerde talen – die voorlopig ook nog wel populair zullen blijven – dan zullen ze toch ook moeten wennen aan functionele aspecten.
Het leren van een nieuwe taal is daarbij niet de grootste uitdaging, maar wel het aanmeten van een andere manier van denken. Wie bij wil blijven als developer, doet er goed aan zich te verdiepen in functionele talen en zich de functionele aspecten van objectgeoriënteerde talen eigen te maken.
Gert Jan Timmerman, manager kenniscentrum bij Info Support,
– zouden functionele talen niet ook veel meer objectgeorienteerde elementen in zich krijgen ?
– zijn objectmethoden niet gewoon functies op het object, dus nog steeds functies met of zonder parameters ?
– hoe kom je op een algemene stelling “Datastructuren zijn binnen functionele talen onveranderbaar (immutable dus)” ?
– waarom wordt immutability zo gekoppeld aan functionele programmeertalen, als de immutable oplossingen in zoveel soorten talen zijn geimplementeerd (immutable object, strings, tuples) ?
– zijn talen als ruby en python functionele talen met veel objectorientatiemogelijkheden of objectoriented talen met veel functionele mogelijkheden ?
– is het werknemervoorbeeld niet een typische database toepassing met CRUD bewerkingen met REST als oplossing voor schaling, die je dan horizontaal doet ? Waarom maar 1 voorbeeld en dan juist deze ?
@Dino, “zouden functionele talen niet ook veel meer objectgeorienteerde elementen in zich krijgen ?” => Er ontstaan steeds meer hybride talen naast de puur functionele en puur oo-talen.
@Dino, -“zijn objectmethoden niet gewoon functies op het object, dus nog steeds functies met of zonder parameters ?” => ja, dat klopt wel, maar functionele talen zijn gebaseerd op het wiskundige functiebegrip. In het wiskundige functiebegrip is een functie een mapping van input-waardes naar output-waardes, zonder side-effects.
-“hoe kom je op een algemene stelling “Datastructuren zijn binnen functionele talen onveranderbaar (immutable dus)” ?” =>Een wiskundige functie brengt geen wijzigingen aan. Vandaar immutability.
-“waarom wordt immutability zo gekoppeld aan functionele programmeertalen, als de immutable oplossingen in zoveel soorten talen zijn geimplementeerd (immutable object, strings, tuples) ?” => Ook binnen andere talen bestaat immutability, maar dat is dan een keuze. Binnen (zuivere) functionele talen zijn datastructuren per definitie immutable.
-“zijn talen als ruby en python functionele talen met veel objectorientatiemogelijkheden of objectoriented talen met veel functionele mogelijkheden ?” => het is maar hoe je er tegenaan kijkt.
-“is het werknemervoorbeeld niet een typische database toepassing met CRUD bewerkingen met REST als oplossing voor schaling, die je dan horizontaal doet ?” => je kunt werknemers in een database opslaan en dan heb je misschien een relationeel model (als je een relationele database gebruikt). In mijn voorbeeld heb ik me beperkt tot het vergelijken van OO en functioneel, maar dat zijn niet de enige mogelijkheden om werknemers te bewerken. Een (relationele) database is ook een mogelijkheid.
-“Waarom maar 1 voorbeeld en dan juist deze ?” => 1 voorbeeld omdat dat voorbeeld het verschil goed duidelijk maakt. Een tweede voorbeeld voegt niet veel toe.
Dino, met je suggestie dat functionele talen ook steeds meer objectgeoriënteerde elementen in zich krijgen onderschat je de stelling van de auteur dat functioneel programmeren “een wezenlijk andere benadering van programmeren” is.
Voor zover je hierin echter gelijk hebt is dat wel een aanwijzing dat functionele programmeertalen niet alleen kunsttalen zijn (want gebaseerd op wiskunde) maar vooral ook nogal gekunsteld. Want eigenlijk horen objecten helemaal niet thuis in een functionele programmeertaal.
Object oriëntatie en functioneel programmeren bewegen zich namelijk in tegenovergestelde richtingen: waar objectoriëntatie probeert “de code zo dicht mogelijk bij de werkelijkheid te laten aansluiten”, daar probeert het functionele paradigma juist van iedere verwijzing naar of contact met de werkelijkheid af te zien. Het functioneel programmeren streeft feitelijk naar een geheugenloze en dus ook toestandsloze machine, terwijl het geheugen bij objectoriëntatie juist die aansluiting met de werkelijkheid realiseert.
Functionele programmeertalen werken strikt volgens het procedure-data paradigma: actieve procedures verwerken passieve data volgens een input-processing-output cyclus. Waarbij een functie een procedure is met één uitvoerparameter die wordt teruggegeven via de functienaam.
Bij object-oriëntatie wordt deze scheiding tussen actieve procedures en passieve data juist opgeheven en wordt het procedure-data paradigma dus doorbroken. Een object wordt dus niet verwerkt door externe procedures en functies maar krijgt de opdracht om een operatie op zichzelf uit te voeren (door het sturen van een bericht of het direct aanroepen van een binnen het object bekende methode). Het object weet dus zelf hoe het een bepaalde operatie dient uit te voeren.
En daarmee moet ik toch een kleine correctie aanbrengen aan dit zeer lezenswaardige opiniestuk:
“Als deze werknemers een salarisverhoging krijgen, pas je, een voor een, de bijbehorende objecten aan.”
Zo geformuleerd bevindt de kennis voor het verwerken van de objecten zich nog geheel aan de kant van de aanroeper, en is de afhandeling volledig procedureel! Object georiënteerd bevindt de kennis zich echter in het aangeroepen object en deze bepaalt vervolgens zelf per individueel geval hoe deze salarisverhoging moet worden afgehandeld. En uiteraard wordt zo’n salarisverhoging door een CEO anders afgehandeld dan een medewerker op de werkvloer.
En daarmee kom ik op een volgend belangrijk verschil tussen functioneel versus objectgeoriënteerd:
waar binnen object oriëntatie de operaties afhankelijk zijn van de intuïtieve betekenissen van de objecten, daar komen bij het functioneel programmeren (en bijvoorbeeld ook in het relationele model) de betekenissen tot stand door wiskundige operaties met de gegevens.
Object georiënteerd valt dit gemakkelijk in te zien: het heeft geen enkele zin om een operatie als salarisverhoging toe te passen op objecten anders dan werknemers! Andersom moet je bij een functionele programmeertaal maar afwachten of je van de nodeloos ingewikkelde formules nog chocola kunt maken.
Even kort:
operationele betekenissen: wiskunde, (formele) logica
intuïtieve betekenissen: natuurlijke taal
Voor een aanvulling op bovenstaande zie mijn reactie onder:
https://www.computable.nl/artikel/nieuws/overheid/6177170/250449/overheid-moet-zich-niet-voordoen-als-ict-bedrijf.html
(al ben ik bang dat je opnieuw hoofdpijn gaat krijgen 🙂
NB: het onderscheid tussen operationele en intuïtieve betekenissen heb ik ontleend aan een publicatie van de Nederlandse cultuurfilosoof C.A. van Peursen (1920-1996): Fenomenologie en analytische filosofie (1968). Dit onderscheid komt zelfs terug in de titel van hoofdstuk 6: “Een eerste confrontatie: operationele of intuïtieve betekenissen”. Dit boek las ik overigens in 1989.
d
Dat meningen niet bij de werkelijkheid aansluiten kan natuurlijk keertje gebeuren, maar pleiten voor een heel paradigma met dat als uitgangspunt.. Kunnen we straks met die functionele talen heel makkelijk parallelle oplossingen verzinnen die niets met de werkelijkheid te maken hebben ? Na de reactie van Jack lijken meerdere voorbeelden dan die van de werknemersalarisverhoging wel wat toe te voegen.
Meester Gert Jan en Jack, wij willen chocola zonder hoofdpijn en onze klanten ook 😛
Dino, sterk opgemerkt; na het plaatsen van mijn vorige reactie zat die salarisverhoging die door een medewerker (-object) wordt afgehandeld mij ook niet lekker. Een salarisverhoging is natuurlijk een periodiek terugkerende taak (ook een object), waar een aantal workflows onder kunnen hangen, bijvoorbeeld voor het aanleveren van bewijsstukken, bonnetjes, wensen, etc. van de betreffende werknemer, akkoordverklaring(en) van leidinggevende(n), het inwinnen van gegevens bij externe instanties, etc., waarbij uiteraard alleen die workflows worden uitgevoerd die betrekking hebben op ontbrekende gegevens. Pas als alle benodigde invoergegevens bekend zijn (wat deze taak zelf bijhoudt), kan het nieuwe salaris worden berekend en worden vastgelegd door het werknemer-object.
Functionele programmeertalen kunnen niet overweg met ontbrekende gegevens; steeds moeten bij aanroep van een functie alle invoervariabelen bekend zijn, en daarmee is een salarisverhoging precies een voorbeeld van een taak die je wel object-georiënteerd en niet functioneel kunt uitvoeren.
Functioneel wordt de indruk gewekt dat salarisverhoging een (batch-)proces is dat in één keer voor de hele populatie kan worden uitgevoerd, omdat alle benodigde invoergegevens al bekend zijn; in werkelijkheid is het een taak die voor iedere werknemer afzonderlijk moet worden afgehandeld.
En dat maakt inderdaad nieuwsgierig naar voorbeelden waar functionele programmeertalen wel met succes toepasbaar zijn.
Functioneel programmeren schopt kont ten opzichte van object georiënteerd programmeren. Het is het zelfde als declarative versus imperative programmeren. Declarative is veel krachtiger. Het verschil is zeggen wat een programma moet doen (imperative) versus declareren hoe het resultaat eruit moet zien (declarative) en het framework / taal het zelf uit laten zoeken. Declarative is in mijn ogen functioneel programmeren.
Ik ben zelf altijd meer object georiënteerd geweest en denken in classes, maar vroeg of laat kwam ik altijd in de knoop.
Functioneel is veel moeilijker en beperkter. Veel dingen kunnen domweg niet. Maar wat kan is altijd sneller, mooier en duurzamer.
Uiteindelijk kun je met functioneel niet alles en zul je af en toe concessies moeten doen. Classes en inheritance zorgen er altijd voor dat je jezelf in een hoekje schildert. Composition over Inheritance dus.
Bronnen:
Eric Elliot (Javascript)
https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c
https://medium.com/javascript-scene/abstraction-composition-cb2849d5bdd6
In my experience, mutating an existing object rather than creating a new object is usually a bug. At the very least, it is error-prone.
Zeer helder verhaal wat in mijn ogen: Funfunfunction – https://www.youtube.com/watch?v=wfMtDGfHWpA
Beetje warrige reactie van me, maar moest het even kwijt….
Erlang FTW.