De producten die we tegenwoordig ontwikkelen worden steeds complexer. Deze complexiteit ontstaat door de toenemende vraag naar nieuwe softwarefuncties, die in steeds kortere tijd geleverd moeten worden. We bereiken een absoluut niveau waarboven we geen software op een beheersbare, kosteneffectieve manier meer kunnen ontwikkelen. Nog meer software-ingenieurs aannemen om software te ontwikkelen is niet langer de oplossing. We bereiken de grenzen van wat mogelijk is.
Door: Paul Zenden, systeem architect bij Sioux in Eindhoven.
Nieuwe producten, nieuwe games en nieuwe diensten. Ze worden allemaal steeds ingewikkelder. Denk aan virtual/augmented reality games, wafersteppers, je smartphone, intelligente auto’s en internet of things in het algemeen. Hoe komt dat?
Nieuwe producten/diensten moeten aan onze verwachtingen voldoen. We raken gewend aan de huidige werking en verwachten dat de volgende versie van het product nog beter is, maar ook nog sneller, mooier, met meer pixels, met altijd en overal internetverbinding, enz. Om een concurrentievoorsprong te behouden moeten nieuwe functies worden toegevoegd, in steeds kortere tijd. Bovendien verwachten we dat applicaties verbinding met elkaar kunnen maken en met elkaar kunnen communiceren (bijvoorbeeld het gebruik van je smartphone om de televisie te kunnen bedienen). Door de groei van de hardware-mogelijkheden neemt dit alleen nog maar toe.
Toch is er een probleem. In sommige domeinen bereiken we al het punt waarop de omvang van de software zo groot wordt dat het bijna onmogelijk is om de controle te behouden. Het loopt uit de hand. We kunnen niet doorgaan met extra software-ingenieurs aan te nemen om de software te ontwikkelen en onderhouden. F. Brooks merkte dit in 1995 al op in ‘The Mythical Man-Month’.
De oplossing ligt in het verminderen van de complexiteit en het ontwikkelen van software op een slimmere manier. Eén van de technologieën die ik onder de aandacht wil brengen is functioneel programmeren. Functioneel programmeren is zeker niet de enige oplossing, ook is het niet nieuw, maar het helpt ons wel om een eerste stap te maken.
Concepten op het gebied van functioneel programmeren zijn al bekend en worden al gebruikt sinds de jaren vijftig van de vorige eeuw, maar zijn de afgelopen decennia niet erg uitgebreid toegepast. Een belangrijke reden hiervoor is dat de beschikbare hardware niet over de rekencapaciteit en het interne geheugen beschikte dat nodig is om industriële oplossingen te ontwikkelen. Destijds was het objectgeoriënteerde paradigma geschikter. Nu we de beschikking hebben over goedkope en krachtige, gedistribueerde, multi-core systemen en goedkoop geheugen, is het toepassen van functioneel programmeren aan een inhaalslag bezig.
Functioneel programmeren werkt met ‘onveranderlijke status data’, functies zonder neveneffecten en de mogelijkheid om complexe functies op te bouwen uit simpelere functies. Functies worden behandeld zoals in de wiskunde (de zogenaamde ‘pure’ functies). Functies gebruiken alleen de invoergegevens, maken er uitvoergegevens van, maar wijzigen de invoergegevens niet. Wanneer functies geen neveneffecten hebben wordt het makkelijker om ze parallel uit te voeren. De prestaties nemen verder toe wanneer ze worden uitgevoerd op een multi-core-systeem.
Onveranderlijke status data betekent dat, eens een variabele een waarde heeft gekregen, deze nooit meer kan veranderen. Door het wijzigen van status data uit te sluiten, kunnen multi-threaded oplossingen worden gemaakt, zonder de veel voorkomende problemen zoals ‘race conditions’ (bij toegang tot gedeeld geheugen) of ‘dead-locks’.
Het lijkt bijna onmogelijk om bruikbare programma’s te schrijven zonder de waarde van variabelen aan te kunnen passen. Maar toch kan het, door pure functies, onveranderlijke variabelen en recursie met elkaar te combineren. Ik daag je uit om dit zelf te onderzoeken.
Functionele programma’s zijn minder complex in de zin dat ze, over het algemeen, dezelfde functionaliteit realiseren als object-georiënteerde programma’s met minder en betere kwaliteit code. (Op Rosetta Code staan veel voorbeelden waarin de functionele programmeertaal oplossing veel korter is dan de oplossing in de object georiënteerde talen.) Dankzij de onveranderlijke status data en functies zonder neveneffecten, heb je minder risico op bugs. Functioneel programmeren is ook een goede manier om gedistribueerde systemen te ontwikkelen, omdat de parallelle uitvoering van functies gemakkelijk te realiseren is.
Maar helaas brengt functioneel programmeren voor veel software-ingenieurs een nieuwe moeilijkheid met zich mee. Het paradigma van functioneel programmeren verschilt namelijk behoorlijk van object-georiënteerd of procedureel programmeren. Om efficiënte functionele code te ontwikkelen moet je eerst de concepten van functioneel programmeren kennen en dit verschil in paradigma overbruggen. En dat is niet zo eenvoudig. Dit houdt op het moment een bredere acceptatie tegen. Maar moet dit ons belemmeren om naar de voordelen van functionele programmeertaal te kijken? Nee. Wanneer je eenmaal een nieuw paradigma, een nieuwe programmeertaal hebt geleerd, heb je er later regelmatig profijt van.
Er zijn heel wat functionele programmeertalen om uit te kiezen, zoals Scala, F#, Erlang, Clojure en nog vele andere. Vaak is het mogelijk om imperatieve programmeertalen met functionele programmeertalen te combineren, en/of een codeerstijl in een imperatieve programmeertaal te gebruiken die leidt tot een ‘functionele’ oplossing van het probleem.
Wist je dat de volgende populaire producten/applicaties allemaal Erlang gebruiken: WhatsApp, RabbitMQ, SimpleDB (Amazon), CouchDB, eJabberd, GPRS/3G mobiele netwerkknooppunten (Ericsson).
Er komen nieuwe, krachtige frameworks uit die veel details afschermen, en die daardoor de toepassing van functionele programmeerconcepten steeds makkelijker maken. Voorbeelden hiervan zijn ELM voor client-side webapplicaties en Phoenix, een server-side webframework. Een andere interessante nieuwkomer is Elixir. Elixir is gebaseerd op Erlang, een volwassen functionele programmeertaal en framework. Erlang wordt al meer dan 20 jaar toegepast in de telecommunicatie. Elixir maakt gebruik van de sterke punten van Erlang, maar met een meer toegankelijke taal die voor een gedeelte is afgeleid van Ruby. Elixir beschikt ook over indrukwekkende meta-programmeerfuncties die de ontwikkeling van zogenaamde interne DSL’s mogelijk maakt. Een andere interessante ontwikkeling is het gebruik van Elixir op het gebied van embedded software.
Functioneel programmeren vermindert de omvang van de code, vermindert het risico op bugs en vergemakkelijkt parallellisatie met een hogere performance als resultaat. Het is een van de beschikbare middelen om ons te helpen de groeiende complexiteit van producten en diensten in de hand te houden. Het is met name een technologie om te overwegen voor parallelle gedistribueerde applicaties.
Functioneel programmeren behoort (nog) niet tot de vaardigheden van veel software-ingenieurs. En dit maakt de toepassing ervan op grote schaal ingewikkeld. We kunnen allemaal helpen hier verandering in te brengen door vandaag te beginnen met het leren van functioneel programmeren!
Meer lezen:
Er bestaan heel wat boeken over functioneel programmeren en online zijn er ook veel cursussen beschikbaar, zoals Coursera, EDX en Plural Sight. De volgende cursus sprak mij erg aan: Erik Meijers 'Funtional Programming Fundamentals'.