torsdag 19. september 2013 Polyglot CoffeeScript Lambda
Du har hørt om denne lambda calculus, men hva i pokker er det for noe?! Er det ikke himla teoretisk, og kan det i det hele tatt brukes til noe?
I denne og de følgende postene kommer jeg til å snakke minimalt om teori, og i stedet vise lambda calculus i praksis. Og hva er det jeg nesten alltid gjør når jeg skal demonstrere? Jo, jeg beregner summen av alle tall under 1000 som er multipler av 3 eller 5. Det skal jeg forsøke her også. Men vi må starte på begynnelsen...
Du kan godt si at lambda calculus er et programmeringsspråk, og som et språk er det ganske begrenset; det inneholder kun tre ting:
Dette er jo ting som alle programmeringsspråk med respekt for seg selv har, så dermed kan vi programmere lambda calculus i et annet språk – om vi bare passer på å ikke bruke noe vi ikke skal.
Et språk som uttrykker anonyme funksjoner veldig elegant er CoffeeScript. I coffee kan man uttrykke lambda nesten like kompakt og elegant som i Alonzo Church sin opprinnelige lambda-syntaks. Men uansett er det ikke syntaksen som er viktig, men konseptene bak. Så dermed bruker jeg altså CoffeeScript.
Vi har altså tre ting vi må kunne uttrykke. Det første er en variabel. Det er det lett å uttrykke i coffee, for eksempel:
x
Og så trenger vi anonyme funksjoner. For eksempel en funksjon som tar inn en verdi og returnerer den:
(x) -> x
Du skjønner hva jeg mener med elegant syntaks nå, ikke sant?! I JavaScript ville det samme sett ut som dette:
function(x) { return x; }
Det siste vi behøver er funksjonskall. Sett at f er en funksjon og x en annen variabel, så er dette et funksjonskall:
f(x)
Tilsvarende i JavaScript:
f(x)
Jepp, det er selvfølgelig helt likt!
Egentlig ikke. Men for det skal være praktisk å demonstrere noe med lambda calculus så vil jeg gjerne ha en ting til, nemlig muligheten til å tilordne et lamdbauttrykk til en variabel. Dette er altså ikke for å gi språket mere kraft på noen måte, men for å slippe å repetere meg selv og for å gjøre koden lesbar.
Jeg kommer også til å lage noen hjelpefunksjoner. Disse er til for å gjøre uttrykkene forståelige for deg og meg, og har ellers ingenting med lambda calculus å gjøre.
Jeg vet ikke om du har lagt merke til det, men jeg har ikke sagt noe om datatyper enda? Det er fordi det kun finnes én type, nemlig funksjonen! For å kunne programmere på en fornuftig måte behøver vi liksom noe mer. Og en av de enkleste datatypene bruker å være boolean. Så da får vi lage den da...
Men hva skal bool være i et språk som bare har funksjoner? En funksjon selvfølgelig. Trust me, dette gir mening! La oss si at en bool er en funksjon med to parametre. La oss anta at TRUE vil returnere det første argumentet, mens FALSE vil returnere det andre argumentet. Da kan vi definere TRUE og FALSE slik:
TRUE = (x, y) -> x FALSE = (x, y) -> y
Her har jeg allerede brutt med reglene for lambda calculus, ettersomjeg har laget funksjoner med flere parametre. Dette fikser vi derimot enkelt:
TRUE = (x) -> (y) -> x FALSE = (x) -> (y) -> y
Det vi har nå er egentlig funksjoner som returnerer funksjoner, men det går for det samme. La oss for ordens skyld se hvordan TRUE ser ut i JavaScript:
TRUE = function(x) { return function(y) { return x; }; };
La oss teste disse verdiene vi har laget:
show = console.log show TRUE(true)(false) show FALSE(true)(false)
Her skriver jeg ut resultatet av å kalle henholdsvis TRUE og FALSE med argumentene true og false. Dette resulterer i to linjer i konsollet med henholdsvis "true" og "false".
Faktisk: la oss lage en hjelpefunksjon som konverterer et boolean-representerende lambdauttrykk til CoffeeScript-verdien true eller false:
toBool = (f) -> f(true)(false)
Nå kan jeg teste med å bruke denne i stedet:
show toBool(TRUE) show toBool(FALSE)
.. som igjen skriver ut "true" og "false".
La oss avslutte denne del 1 med noe litt kult! Jeg skal nå lage en if/else-konstruksjon i lambda calculus. Og den er enkel:
IF = (b) -> b
If er rett og slett en funksjon som returnerer argumentet sitt, som jeg antar er en funksjon som representerer en boolean. La oss se hvordan den ser ut i bruk:
show IF(TRUE)("yes")("no") show IF(FALSE)("yes")("no")
Som man kan forvente består if/else-konstruksjon av tre deler: et boolsk uttrykk, resultatet når uttrykket er sant, og resultatet når uttrykket ikke er sant. Testen skriver ut "yes" og "no".
Jeg håper dette på en eller annen måte gav mersmak. I neste kaffepause vil jeg vise hvordan man lager tall i lambda calculus, så følg med!