Beknoptheid is Kracht

Mei 2002

| "De hoeveelheid betekenis die in een kleine ruimte wordt gecomprimeerd door algebraïsche tekens, is nog een omstandigheid die de redeneringen die we gewend zijn ermee uit te voeren, vergemakkelijkt."

  • Charles Babbage, geciteerd in Iverson's Turing Award Lecture

In de discussie over kwesties die door Revenge of the Nerds op de LL1 mailinglijst werden opgeworpen, schreef Paul Prescod iets dat me bijbleef.

Python's doel is regelmatigheid en leesbaarheid, niet beknoptheid.

Op het eerste gezicht lijkt dit een nogal vernietigende uitspraak over een programmeertaal. Voor zover ik kan nagaan, geldt beknoptheid = kracht. Zo ja, dan substituerend krijgen we

Python's doel is regelmatigheid en leesbaarheid, niet kracht.

en dit lijkt geen ruil (als het al een ruil is) die je zou willen maken. Het is niet ver verwijderd van te zeggen dat Python's doel niet is om effectief te zijn als programmeertaal.

Is beknoptheid = kracht? Dit lijkt me een belangrijke vraag, misschien wel de belangrijkste vraag voor iedereen die geïnteresseerd is in taalontwerp, en een die nuttig zou zijn om direct aan te pakken. Ik ben er nog niet zeker van dat het antwoord een simpel ja is, maar het lijkt een goede hypothese om mee te beginnen.

Hypothese

Mijn hypothese is dat beknoptheid kracht is, of er zo dichtbij ligt dat je ze, behalve in pathologische voorbeelden, als identiek kunt beschouwen.

Het lijkt me dat beknoptheid is waar programmeertalen voor zijn. Computers zouden er net zo blij mee zijn om direct in machinecode te worden verteld wat ze moeten doen. Ik denk dat de belangrijkste reden dat we de moeite nemen om high-level talen te ontwikkelen, is om hefboomwerking te krijgen, zodat we in 10 regels van een high-level taal kunnen zeggen (en belangrijker nog, denken) wat 1000 regels machinecode zou vereisen. Met andere woorden, het belangrijkste punt van high-level talen is om broncode kleiner te maken.

Als kleinere broncode het doel is van high-level talen, en de kracht van iets is hoe goed het zijn doel bereikt, dan is de maatstaf voor de kracht van een programmeertaal hoe klein het je programma's maakt.

Omgekeerd doet een taal die je programma's niet klein maakt een slechte job van wat programmeertalen zouden moeten doen, zoals een mes dat niet goed snijdt, of drukwerk dat onleesbaar is.

Metrics

Klein in welke zin dan? De meest voorkomende maatstaf voor codegrootte zijn regels code. Maar ik denk dat deze maatstaf de meest voorkomende is omdat deze het gemakkelijkst te meten is. Ik denk niet dat iemand echt gelooft dat dit de ware test is van de lengte van een programma. Verschillende talen hebben verschillende conventies over hoeveel je op een regel moet zetten; in C staan veel regels leeg op een scheidingsteken of twee na.

Een andere gemakkelijke test is het aantal tekens in een programma, maar dit is ook niet erg goed; sommige talen (zoals Perl) gebruiken gewoon kortere identifiers dan andere.

Ik denk dat een betere maatstaf voor de grootte van een programma het aantal elementen zou zijn, waarbij een element alles is wat een afzonderlijk knooppunt zou zijn als je een boom tekent die de broncode vertegenwoordigt. De naam van een variabele of functie is een element; een geheel getal of een drijvende-kommagetal is een element; een stuk letterlijke tekst is een element; een element van een patroon, of een formaatrichtlijn, is een element; een nieuw blok is een element. Er zijn grensgevallen (zijn -5 twee elementen of één?) maar ik denk dat de meeste daarvan voor elke taal hetzelfde zijn, dus ze beïnvloeden vergelijkingen niet veel.

Deze maatstaf moet worden uitgewerkt, en het kan interpretatie vereisen in het geval van specifieke talen, maar ik denk dat het probeert te meten wat het juiste is, namelijk het aantal onderdelen dat een programma heeft. Ik denk dat de boom die je in deze oefening zou tekenen, is wat je in je hoofd moet maken om het programma te bedenken, en dus is de grootte ervan evenredig met de hoeveelheid werk die je moet doen om het te schrijven of te lezen.

Ontwerp

Dit soort maatstaf zou ons in staat stellen om verschillende talen te vergelijken, maar dat is, althans voor mij, niet de belangrijkste waarde. De belangrijkste waarde van de beknoptheidstest is als leidraad bij het ontwerpen van talen. De meest nuttige vergelijking tussen talen is tussen twee potentiële varianten van dezelfde taal. Wat kan ik doen in de taal om programma's korter te maken?

Als de conceptuele belasting van een programma evenredig is met zijn complexiteit, en een gegeven programmeur een vaste conceptuele belasting kan verdragen, dan is dit hetzelfde als vragen: wat kan ik doen om programmeurs het meeste te laten doen? En dat lijkt me identiek aan vragen: hoe ontwerp ik een goede taal?

(Overigens, niets maakt duidelijker dat de oude gevleugelde uitspraak "alle talen zijn equivalent" onjuist is dan het ontwerpen van talen. Wanneer je een nieuwe taal ontwerpt, vergelijk je constant twee talen - de taal als ik x deed, en als ik het niet deed - om te beslissen welke beter is. Als dit echt een zinloze vraag was, kon je net zo goed een munt opgooien.)

Streven naar beknoptheid lijkt een goede manier om nieuwe ideeën te vinden. Als je iets kunt doen dat veel verschillende programma's korter maakt, is het waarschijnlijk geen toeval: je hebt waarschijnlijk een nuttige nieuwe abstractie ontdekt. Je zou zelfs een programma kunnen schrijven om te helpen door broncode te zoeken naar herhaalde patronen. Van de andere talen zouden die met een reputatie voor beknoptheid degene zijn om naar te kijken voor nieuwe ideeën: Forth, Joy, Icon.

Vergelijking

De eerste persoon die over deze kwesties schreef, voor zover ik weet, was Fred Brooks in de Mythical Man Month. Hij schreef dat programmeurs ongeveer dezelfde hoeveelheid code per dag leken te genereren, ongeacht de taal. Toen ik dit voor het eerst las in mijn vroege twintiger jaren, was het een grote verrassing voor me en leek het enorme implicaties te hebben. Het betekende dat (a) de enige manier om software sneller te schrijven was om een beknoptere taal te gebruiken, en (b) iemand die de moeite nam om dit te doen, concurrenten die dat niet deden, kon achterlaten.

Brooks' hypothese, als deze waar is, lijkt de kern van hacking te raken. In de jaren daarna heb ik nauwlettend aandacht besteed aan elk bewijs dat ik kon krijgen over de vraag, van formele studies tot anekdotes over individuele projecten. Ik heb niets gezien dat hem tegenspreekt.

Ik heb nog geen bewijs gezien dat mij doorslaggevend leek, en ik verwacht het ook niet. Studies zoals de vergelijking van programmeertalen door Lutz Prechelt, hoewel ze het soort resultaten genereren dat ik verwachtte, hebben de neiging om problemen te gebruiken die te kort zijn om zinvolle tests te zijn. Een betere test van een taal is wat er gebeurt in programma's die een maand duren om te schrijven. En de enige echte test, als je gelooft zoals ik dat het belangrijkste doel van een taal is om goed in te denken (in plaats van alleen een computer te vertellen wat te doen als je het eenmaal hebt bedacht), is wat voor nieuwe dingen je erin kunt schrijven. Dus elke taalvergelijking waarbij je moet voldoen aan een vooraf gedefinieerde specificatie test een beetje het verkeerde ding.

De ware test van een taal is hoe goed je nieuwe problemen kunt ontdekken en oplossen, niet hoe goed je het kunt gebruiken om een probleem op te lossen dat iemand anders al heeft geformuleerd. Deze twee zijn heel verschillende criteria. In kunst werken media zoals borduurwerk en mozaïek goed als je van tevoren weet wat je wilt maken, maar zijn absoluut slecht als je dat niet weet. Als je het beeld wilt ontdekken terwijl je het maakt - zoals je moet doen met iets zo complex als een afbeelding van een persoon, bijvoorbeeld - moet je een vloeiender medium gebruiken zoals potlood of aquarel of olieverf. En inderdaad, de manier waarop wandtapijten en mozaïeken in de praktijk worden gemaakt, is om eerst een schilderij te maken en het vervolgens te kopiëren. (Het woord "cartoon" werd oorspronkelijk gebruikt om een schilderij te beschrijven dat voor dit doel bedoeld was).

Wat dit betekent, is dat we waarschijnlijk nooit nauwkeurige vergelijkingen zullen hebben van de relatieve kracht van programmeertalen. We zullen precieze vergelijkingen hebben, maar geen nauwkeurige. In het bijzonder zullen expliciete studies met het doel talen te vergelijken, omdat ze waarschijnlijk kleine problemen zullen gebruiken en noodzakelijkerwijs vooraf gedefinieerde problemen zullen gebruiken, de kracht van de krachtigere talen onderschatten.

Rapportages uit het veld, hoewel ze noodzakelijkerwijs minder precies zullen zijn dan "wetenschappelijke" studies, zullen waarschijnlijk zinvoller zijn. Ulf Wiger van Ericsson deed bijvoorbeeld een studie die concludeerde dat Erlang 4-10 keer beknopter was dan C++, en proportioneel sneller om software in te ontwikkelen:

Vergelijkingen tussen Ericsson-interne ontwikkelprojecten geven vergelijkbare lijn/uur productiviteit aan, inclusief alle fasen van softwareontwikkeling, vrijwel onafhankelijk van welke taal (Erlang, PLEX, C, C++, of Java) werd gebruikt. Wat de verschillende talen dan onderscheidt, is het broncodevolume.

De studie behandelt ook expliciet een punt dat in Brooks' boek slechts impliciet was (aangezien hij regels gedebugde code mat): programma's geschreven in krachtigere talen hebben de neiging minder bugs te hebben. Dat wordt een doel op zich, mogelijk belangrijker dan programmeurproductiviteit, in toepassingen zoals netwerkswitches.

De Smaak Test

Uiteindelijk denk ik dat je op je gevoel moet afgaan. Hoe voelt het om in de taal te programmeren? Ik denk dat de manier om de beste taal te vinden (of te ontwerpen) is om hypersensitief te worden voor hoe goed een taal je laat denken, en vervolgens de taal te kiezen/ontwerpen die het beste aanvoelt. Als een taalfeature onhandig of beperkend is, maak je geen zorgen, je zult het weten.

Zo'n hypersensitiviteit zal een prijs hebben. Je zult merken dat je niet kunt programmeren in lompe talen. Ik vind het ondraaglijk beperkend om te programmeren in talen zonder macro's, net zoals iemand die gewend is aan dynamische typing het ondraaglijk beperkend vindt om terug te moeten gaan naar programmeren in een taal waar je het type van elke variabele moet declareren, en geen lijst van objecten van verschillende types kunt maken.

Ik ben niet de enige. Ik ken veel Lisp hackers bij wie dit is gebeurd. Sterker nog, de meest nauwkeurige maatstaf voor de relatieve kracht van programmeertalen zou het percentage mensen kunnen zijn die de taal kennen en elke baan accepteren waar ze die taal mogen gebruiken, ongeacht het toepassingsdomein.

Restrictie

Ik denk dat de meeste hackers weten wat het betekent als een taal beperkend aanvoelt. Wat gebeurt er als je dat gevoel hebt? Ik denk dat het hetzelfde gevoel is als wanneer de straat die je wilt nemen is afgesloten, en je een lange omweg moet nemen om te komen waar je wilde zijn. Er is iets dat je wilt zeggen, en de taal laat je niet toe.

Wat hier echt aan de hand is, denk ik, is dat een beperkende taal er een is die niet beknopt genoeg is. Het probleem is niet simpelweg dat je niet kunt zeggen wat je van plan was. Het is dat de omweg die de taal je laat nemen langer is. Probeer dit gedachte-experiment. Stel dat er een programma was dat je wilde schrijven, en de taal liet je het niet uitdrukken zoals je van plan was, maar dwong je in plaats daarvan het programma op een andere manier te schrijven die korter was. Voor mij zou dat in ieder geval niet erg beperkend aanvoelen. Het zou zijn als de straat die je wilde nemen afgesloten is, en de politieagent op het kruispunt je naar een kortere weg stuurt in plaats van een omweg. Geweldig!

Ik denk dat de meeste (negentig procent?) van het gevoel van beperking voortkomt uit het gedwongen worden om het programma dat je in de taal schrijft langer te maken dan een dat je in je hoofd hebt. Beperking is grotendeels gebrek aan beknoptheid. Dus als een taal beperkend aanvoelt, betekent dat (meestal) dat het niet beknopt genoeg is, en als een taal niet beknopt is, zal het beperkend aanvoelen.

Leesbaarheid

Het citaat waarmee ik begon, noemt twee andere kwaliteiten, regelmatigheid en leesbaarheid. Ik weet niet zeker wat regelmatigheid is, of welk voordeel, indien aanwezig, code die regelmatig en leesbaar is heeft ten opzichte van code die slechts leesbaar is. Maar ik denk dat ik weet wat er met leesbaarheid wordt bedoeld, en ik denk dat het ook verband houdt met beknoptheid.

We moeten hier voorzichtig zijn om onderscheid te maken tussen de leesbaarheid van een individuele regel code en de leesbaarheid van het hele programma. Het is het tweede dat ertoe doet. Ik ben het ermee eens dat een regel Basic waarschijnlijk leesbaarder is dan een regel Lisp. Maar een programma geschreven in Basic zal meer regels hebben dan hetzelfde programma geschreven in Lisp (vooral als je overstapt naar Greenspunland). De totale inspanning om het Basic-programma te lezen zal zeker groter zijn.

totale inspanning = inspanning per regel x aantal regels

Ik ben er niet zo zeker van dat leesbaarheid direct evenredig is met beknoptheid als ik dat ben van kracht, maar zeker beknoptheid is een factor (in de wiskundige zin; zie bovenstaande vergelijking) in leesbaarheid. Dus het is misschien niet eens zinvol om te zeggen dat het doel van een taal leesbaarheid is, niet beknoptheid; het zou kunnen zijn als zeggen dat het doel leesbaarheid was, niet leesbaarheid.

Wat leesbaarheid per regel wel betekent, voor de gebruiker die de taal voor het eerst tegenkomt, is dat broncode er onbeangstigend uitziet. Dus leesbaarheid per regel kan een goede marketingbeslissing zijn, zelfs als het een slechte ontwerpbeslissing is. Het is isomorf aan de zeer succesvolle techniek van het laten betalen in termijnen: in plaats van hen te beangstigen met een hoge aanbetaling, vertel je hen de lage maandelijkse betaling. Termijnplannen zijn echter een nettoverlies voor de koper, net zoals louter leesbaarheid per regel waarschijnlijk is voor de programmeur. De koper zal een heleboel van die lage, lage betalingen doen; en de programmeur zal een heleboel van die individueel leesbare regels lezen.

Deze afweging gaat vooraf aan programmeertalen. Als je gewend bent aan het lezen van romans en krantenartikelen, kan je eerste ervaring met het lezen van een wiskundig artikel ontmoedigend zijn. Het kan een half uur duren om een enkele pagina te lezen. En toch ben ik er vrij zeker van dat de notatie niet het probleem is, ook al voelt het misschien wel zo. Het wiskundige artikel is moeilijk te lezen omdat de ideeën moeilijk zijn. Als je dezelfde ideeën in proza zou uitdrukken (zoals wiskundigen moesten doen voordat ze beknopte notaties ontwikkelden), zouden ze niet gemakkelijker te lezen zijn, omdat het artikel zou uitgroeien tot de omvang van een boek.

Tot welke mate?

Een aantal mensen hebben het idee dat beknoptheid = kracht afgewezen. Ik denk dat het nuttiger zou zijn, in plaats van simpelweg te beweren dat ze hetzelfde zijn of niet, om te vragen: tot welke mate is beknoptheid = kracht? Want duidelijk is beknoptheid een groot deel van waar higher-level talen voor zijn. Als dat niet alles is waarvoor ze zijn, waar zijn ze dan nog meer voor, en hoe belangrijk zijn deze andere functies relatief gezien?

Ik stel dit niet alleen voor om het debat beschaafder te maken. Ik wil echt het antwoord weten. Wanneer, zo ja, is een taal te beknopt voor zijn eigen bestwil?

De hypothese waarmee ik begon was dat, behalve in pathologische voorbeelden, ik dacht dat beknoptheid als identiek met kracht kon worden beschouwd. Wat ik bedoelde was dat in elke taal die iemand zou ontwerpen, ze identiek zouden zijn, maar dat als iemand een taal expliciet zou willen ontwerpen om deze hypothese te weerleggen, ze dat waarschijnlijk zouden kunnen doen. Ik ben daar eigenlijk niet eens zeker van.

Talen, geen Programma's

We moeten duidelijk zijn dat we het hebben over de beknoptheid van talen, niet van individuele programma's. Het is zeker mogelijk dat individuele programma's te dicht worden geschreven.

Ik schreef hierover in On Lisp. Een complexe macro moet misschien vele malen zijn eigen lengte besparen om gerechtvaardigd te zijn. Als het schrijven van een bewerkelijke macro je tien regels code zou kunnen besparen elke keer dat je hem gebruikt, en de macro zelf tien regels code is, dan krijg je een netto besparing in regels als je hem meer dan eens gebruikt. Maar dat kan nog steeds een slechte zet zijn, omdat macrodefinities moeilijker te lezen zijn dan gewone code. Je moet de macro misschien tien of twintig keer gebruiken voordat hij een netto verbetering in leesbaarheid oplevert.

Ik weet zeker dat elke taal zulke afwegingen heeft (hoewel ik vermoed dat de inzet hoger wordt naarmate de taal krachtiger wordt). Elke programmeur moet code hebben gezien die een slim persoon marginaal korter heeft gemaakt door twijfelachtige programmeertrucs te gebruiken.

Dus daar is geen discussie over - althans niet van mij. Individuele programma's kunnen zeker te beknopt zijn voor hun eigen bestwil. De vraag is, kan een taal dat zijn? Kan een taal programmeurs dwingen om code te schrijven die kort (in elementen) is ten koste van de algehele leesbaarheid?

Een reden waarom het moeilijk voor te stellen is dat een taal te beknopt is, is dat als er een overdreven compacte manier zou zijn om iets te formuleren, er waarschijnlijk ook een langere manier zou zijn. Als je bijvoorbeeld vindt dat Lisp-programma's die veel macro's of hogere-orde functies gebruiken te dicht zijn, kun je, als je dat verkiest, code schrijven die isomorf is aan Pascal. Als je faculteit in Arc niet wilt uitdrukken als een aanroep naar een hogere-orde functie (rec zero 1 * 1-) kun je ook een recursieve definitie uitschrijven: (rfn fact (x) (if (zero x) 1 (* x (fact (1- x))))) Hoewel ik uit mijn hoofd geen voorbeelden kan bedenken, ben ik geïnteresseerd in de vraag of een taal te beknopt kan zijn. Zijn er talen die je dwingen om code te schrijven op een manier die krampachtig en onbegrijpelijk is? Als iemand voorbeelden heeft, zou ik ze erg graag willen zien.

(Herinnering: Wat ik zoek zijn programma's die erg dicht zijn volgens de hierboven geschetste "elementen"-maatstaf, niet alleen programma's die kort zijn omdat scheidingstekens kunnen worden weggelaten en alles een één-karakter naam heeft.)