Symposium Verslag: The Future of Programming, Delft 16, 17 Jan 2014

 
06 februari 2014

Op 17 januari 2014 werd Eelco Visser geïnaugureerd als Antoni van Leeuwenhoek Professor of Computer Science aan de TU Delft; een mooie gelegenheid voor de universiteit om een symposium te organiseren over de toekomst van het programmeren. Het programma was veelbelovend en ik kon de kans om een kijkje te nemen in de wereld van de voorlopers van ons vakgebied niet laten lopen. Hoewel ik niet alle sessies mee heb kunnen maken, wil ik bij deze mijn impressies delen, om hopelijk een klein voorproefje te kunnen geven van hoe de toekomst van het programmeren eruit zal gaan zien (tenminste volgens de sprekers die ik heb gehoord).

Als ik een hoofdthema van het symposium zou moeten kiezen, zou het het volgende zijn: het conflict dat software ontwikkelaars ervaren tussen de expressiviteit van programmeertalen en de performance van de programma’s die ermee geschreven kunnen worden. Een expressieve programmeertaal geeft de ontwikkelaar de mogelijkheid om de wat van een programma makkelijk uit te drukken zonder teveel aandacht te hoeven besteden aan de hoe. Bijvoorbeeld, functionele programmeertalen worden vaak gezien als meer expressief dan hun imperatieve tegenhangers, maar als het om performance gaat zijn imperatieve algoritmes vaak beter. Een concreet voorbeeld hiervan is de Collections API van Scala. Naar buiten toe is deze API volledig functioneel, maar van binnen zijn de methodes vaak op een imperatieve manier uitgewerkt (bijv: als je over een lijst wil itereren, kan dit beter met een loop dan met recursie).

Een andere manier om de expressiviteit van een programmeertaal te verhogen is om gebruik te maken van een krachtig type systeem. Een tekort van types is echter dat ze misleidend kunnen zijn als een programma neveneffecten heeft. Over de toekomst van types hadden twee sprekers ideeën. Volgens hen kan de semantiek van een programma in een getypeerde taal alleen volledig beschreven worden als de types iets zeggen over deze neveneffecten.

De toekomst van testen had ook zijn plaats in het symposium. Zoals wij het inmiddels allemaal geleerd hebben, zijn de begrippen “ontwikkelen” en “testen” niet meer van elkaar te scheiden. En op dit gebied was er ook wat nieuws te leren.

Ik zal nu de hierboven geintroduceerde onderwerpen verder toelichten.

De toekomst van testen

Het schrijven van unit tests is een onvermijdelijk onderdeel van het ontwikkelingsproces. Unit tests betekenen niet alleen een hogere kwaliteit en een betere onderhoudbaarheid van code, maar kunnen de ontwikkelaar ook helpen om zijn programma beter te begrijpen. Maar hoe weet je wanneer je genoeg unit tests hebt geschreven, en dat je alle corner cases te pakken hebt?

Het antwoord van John Hughes is “nooit”. Volgens hem is de grootste valkuil van de traditionele manier van unit testen dat de test data altijd dezelfde is en, bovendien, dat hij door de ontwikkelaar zelf is bedacht. Dit betekent dat traditionele unit tests nooit voor een volledige dekking van de functionaliteit van een programma kunnen zorgen. De oplossing die hij voorstelt is “property based testing”. Deze aanpak komt oorspronkelijk uit de Haskell framework QuickCheck, maar bestaat nu in andere smaken (zie ScalaCheck). Met behulp van zo’n framework schrijft de ontwikkelaar geen geïsoleerde test cases meer. In plaats daarvan definieert hij generieke eigenschappen van zijn programma samen met functies die willekeurige data genereren. Op deze manier is de framework in staat om zelf test data te verzinnen en vervolgens te toetsen of de gewenste eigenschappen van het programma daadwerkelijk gelden. De frameworks bieden ook mogelijkheden om de distributie van de test data te beïnvloeden zodat de corner cases opgezocht kunnen worden. Hieronder een voorbeeld van de definitie van een eigenschap binnen ScalaCheck om de implementatie van een binomial heap te toetsen (uit de coursera Reactive Programming cursus).


property("min1") = forAll { a: Int =>
  val h = insert(a, empty)
  findMin(h) == a
}

In dit voorbeeld worden de implementaties van de functies “insert” en “findMin” getoetst. De volgende eigenschap wordt hier gecontroleerd: voor elke int a, als a toegevoegd wordt aan de lege heap, dan moet a de kleinste element van de heap zijn. Het genereren van willekeurige ints wordt door de framework verzorgd.

De toekomst van types

Op de vrijdag ochtend gaaf Erik Meijer een energieke presentatie die veel onderwerpen raakte. Maar een van zijn belangrijkste boodschappen was dat types heel misleidend kunnen zijn in een taal die neveneffecten toe laat. Zijn uitgangspunt was het begrip “functie” zoals het bedoeld is binnen de wiskunde, namelijk als een mapping van de elementen van een set naar de elementen van een andere set. Hij beweert dat de functies waarmee wij programmeren ook aan deze definitie moeten voldoen als wij goed over onze programma’s willen kunnen redeneren. Om te laten zien hoe ver verwijderd onze huidige talen van deze utopie zijn (behalve Haskell misschien) gaf hij een grote reeks aan voorbeelden. Opmerkelijk aan deze voorbeelden was dat de types de semantiek van de functies leken uit te drukken terwijl ze stiekem heel veel gedrag van de functies verborgen.

Twee hele simpele voorbeelden zijn functies die of geen argumenten nemen, of die void retourneren. In beide gevallen zijn deze functies of triviaal (ze doen niets) of “grote leugenaars” (zoals Meijer ze noemde). De reden dat we zulke functies schrijven is dat we geïnteresseerd zijn in hun neveneffecten (bijv. het schrijven van data naar een text bestand). Dit is een stukje semantiek wat volledig ontbreekt van de type informatie.

Een wat subtieler voorbeeld is het volgende:


public int divide(int num, int denom) {
  return (num/denom);
}

Wat is er hier mis met de types? Probeer maar eens divide(3, 0) aan te roepen en kijk of je een int terug krijgt.

De dag ervoor, op de donderdagmiddag, had Daan Leijen (ex collega van Meijer) al een stap in de goede richting gezet om iets te doen aan de oneerlijkheid van types. Bij Microsoft is hij bezig met een experimentele programmeertaal, Koka, die dit probleem verhelpt. Het idee van Koka is dat types ook iets moeten zeggen over de neveneffecten van een functie. Hier zijn twee voorbeelden uit zijn presentatie (de syntax van Koka is javascript-achtig):


function sqr1(x: Int) : total int
{
  return x*x
}

function sqr2(x: Int) : io int
{
  print(“not so secret side effect.”)
  return x*x
}

De eerste functie heeft geen neveneffecten, en dit wordt duidelijk aangeven door de effect type “total”. De tweede functie heeft wel neveneffecten en dit wordt aangegeven door de effect type “io”. Er zijn nog andere effect typen die o.a. aangeven of een functie de staat van objecten in de heap beïnvloedt, en of een functie een exceptie kan gooien. Op deze manier zou het mogelijke gedrag van de boven gedefinieerde “divide” functie beter beschreven kunnen worden. Koka heeft zelfs “type inference” die effect typen kan afleiden.

De toekomst van programmeren: Expressiviteit vs Performance

Als laatste, de hoofdvraag van de conferentie: hoe kun je de expressiviteit van een programmeertaal verhogen zonder te veel performance in te moeten leveren? Wat de algemene aanpak betreft, is iedereen het er mee eens: dat je tools moet aanbieden die de voorkant van een programmeertaal (de syntax) scheiden van de achterkant (de executeerbare code die daar uit gegenereerd wordt). Er zijn echter verschillende meningen over hoe zo’n tool eruit moet zien.

Aan de ene kant zijn er de vertegenwoordigers van “language workbenches”, zoals MPS (van jetBrains), Xtext en Spoofax (waar Eelco Visser zelf aan heeft gewerkt). Zulke tools bieden de mogelijkheid om een eigen taal te definiëren (een domain specific language (DSL)) die heel dicht op de begrippen van een bepaald domein zit en daardoor heel geschikt is om dat domein te modelleren. De tools maken het ook (redelijk) eenvoudig om een eigen compiler voor de taal te bouwen die vervolgens hele performante code kan genereren (bijv. C code). Op deze manier kun je de expressiviteit van een DSL combineren met de performance van een meer low-level taal zoals C. Markus Völter geeft als voorbeeld hiervan het domein van embedded software voor koelkasten. Dit voorbeeld wordt verder uitgewerkt in zijn boek DSL Engineering . Zie ook de laatste podcast van Software Engineering Radio. Brandon Hill gaf ook een presentatie over zijn ervaringen met het gebruik van Spoofax om een DSL te ontwikkelen die een network communicatie protocol beschrijft. Over het algemeen was hij positief, hoewel hij waarschuwde dat er een “steep learning curve” was voor het leren van Stratego, de code transformatie taal die binnen Spoofax gebruikt wordt.

Een hele andere aanpak die probeert expressiviteit te combineren met performance werd vertegenwoordigd door Tiark Rompf, die samenwerkt met het team van Martin Odersky in EPFL. Hun idee is om de mogelijkheden die Scala biedt om embedded DSL’s te definiëren binnen een Scala programma te gebruiken om daar vervolgens performante code uit te genereren. Met “performante code” moet je niet denken aan performante bytecode voor de JVM, maar bijvoorbeeld C code. Tijdens zijn presentatie gaaf Tiark een demonstratie van hoe je heel makkelijk een stuk Scala code kon “compileren” naar een ander stuk Scala code door gebruik te maken van de daarvoor ontwikkelde libary, Lightweight Modular Staging.

Hoewel ze allebei hetzelfde doel hebben, zijn er toch grote verschillen tussen de twee aanpakken. De “language workbenches” geven veel meer vrijheid om de syntax van een DSL te definiëren, en daardoor kunnen ze gebruikt worden om programmeertalen te ontwikkelen die veel beter de begrippen van een bepaald domein omvatten. Op deze manier is het zelfs te hopen dat domein experts (met weinig ervaring met het schrijven van code) aan de slag kunnen met de nieuwe talen. Bovendien zijn de language workbenches vaak gebaseerd op een bestaande code editor (zoals Eclipse) en bieden dus alle verwachte hulpmiddelen aan de gebruiker van de DSL, zoals “syntax highlighting” en “code completion”. De embedded DSL’s van Scala zijn uiteindelijk gelimiteerd door de syntax en het type systeem van Scala zelf. Daartegenover, is de Scala aanpak veel lichtgewichter dan die van de language workbenches. De Lightweight Modular Staging aanpak is gebaseerd op een Scala library, en eist daardoor minder extra-kennis van de ontwikkelaar die daarmee aan de slag gaat (gegeven natuurlijk dat hij een goede kennis van Scala zelf heeft). Om de “language workbenches” te kunnen gebruiken moet de ontwikkelaar eerst best wat kennis opdoen. Onderweg zal hij geconfronteerd worden met het definiëren van een grammatica voor zijn DSL, en zal hij moeten leren omgaan met een infrastructuur die de code generatie slag mogelijk maakt, zoals Stratego in het geval van Spoofax, of de Eclipse Modeling Framework (EMF) in het geval van Xtext.

Laatste gedachten

Ik heb veel inspiratie uit de conferentie kunnen halen, en mijn nieuwsgierigheid werd geprikkeld door de mogelijkheden die language workbenches hebben te bieden. Omdat het de beste tutorials leek te hebben ben ik zelfs gaan experimenteren met Xtext. Ik voelde meteen dat ik een hele krachtige tool te pakken had, maar daarmee kwam ook het besef dat een DSL en bijhorende code generator niet 1-2-3 geschreven is. Verder ben ik ook bewuster geworden van het gat dat bestaat tussen de academische wereld en de wereld van entreprise software ontwikkeling. De academische wereld is een rijke bron van inspiratie, maar het toepassen van zulke ideeën in het veld lijkt mij een hele ambitieuze onderneming.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development, Overige talen en platformen, Open Source, andere platformen