tirsdag 13. desember 2011 Julekalender Polyglot CoffeeScript
CoffeeScript er et rykende ferskt språk som rett og slett forsøker å fikse alle problemene med JavaScript. Koden kompileres ned til JavaScript, og har en syntaks inspirert av Ruby, Python og Haskell. CoffeeScript gir deg som webutvikler også noen nye muligheter som list comprehensions, pattern matching, og klasse-basert objektorientering.
CoffeeScript er mye mer kompakt og elegant enn JavaScript, og har blitt godt mottatt av mange utviklere. Språket er blant annet inkludert i Ruby on Rails, og er også mye brukt på serversiden i Node.js. For noen måneder siden gikk også utviklingsteamet mitt over fra JavaScript til å skrive CoffeeScript, uten nevneverdige problemer.
CoffeeScript-compilatoren er skrevet i JavaScript, og selve kompileringen av scriptene kan derfor skje i browseren. Men dette er selvsagt ikke optimalt – det normale er at man som en del av build-prosessen genererer JavaScript-filer fra CoffeeScript'ene.
For .NET, som er vår plattform, finnes det også HTTP-handlers som kompilerer CoffeeScript on the fly, og server ut JavaScript. Dette er veldig praktisk under selve utviklingen.
Da er det på tide å løse Euler1-oppgaven igjen. Jeg skriver her CoffeeScript-kode som jeg tenker vil bli konvertert til JavaScript og plassert i en webside. Svaret vises ved hjelp av en alert-box.
Her er en enkel, imperativ kodesnutt som finner summen av alle multipler av 3 eller 5 under 1000:
2 answer = 0 3 for n in [1...1000] 4 if n % 3 is 0 or n % 5 is 0 5 answer += n 6 alert answer
Koden kompileres til JavaScript'et nedenfor. Her ser du at CoffeeScript blant annet har "ranges" man kan iterere over, og at koden bruker ord som is og or for å bli mere leservennlig. Du kan også se at den kompilerte koden følger best practises, og er wrappet inn i et funksjonskall, slik at det ikke opprettes noen variabler i det globale navnerommet.
10 (function() { 11 var answer, n; 12 answer = 0; 13 for (n = 1; n < 1000; n++) { 14 if (n % 3 === 0 || n % 5 === 0) { 15 answer += n; 16 } 17 } 18 alert(answer); 19 }).call(this);
Nedenfor har jeg laget en løsning til, som viser hvordan man oppretter funksjoner i CoffeeScript. Den bruker også en if bak et uttrykk, en teknikk arvet fra Ruby. Og numbers-variabelen settes ved hjelp av en list comprehension som utfører selve filtreringen.
10 multipleOf = (n, d) -> n % d is 0 11 12 multipleOf3or5 = (n) -> 13 for divisor in [3,5] 14 return yes if multipleOf(n, divisor) 15 16 numbers = (n for n in [1...1000] when multipleOf3or5 n) 17 18 answer = 0 19 answer += n for n in numbers 20 alert "The answer is #{answer}!"
Den nest siste linjen som summerer opp tallene er også litt snedig, og den aller siste linjen viser at CoffeeScript støtter string interpolation. Og å kunne returnere "yes" i stedet for true fra multipleOf3or5 er jo også litt søtt da :)
JavaScript er jo et språk som støtter prototype-basert objektorientering. Her viser jeg hvordan jeg kan bruke dette i CoffeScript for å løse Euler1-oppgaven. Først oppretter jeg en isMultipleOf-metode på alle tall. Deretter kan jeg lage en fin multipleOf3or5-funksjon som bruker dette.
24 Number::isMultipleOf = (d) -> this % d is 0 25 26 multipleOf3or5 = (n) -> 27 n.isMultipleOf(3) or n.isMultipleOf(5) 28 29 Array::sum = -> 30 answer = 0 31 answer += n for n in this 32 return answer 33 34 alert do (n for n in [1...1000] when multipleOf3or5 n).sum
Den tredje funksjonen opprettes som en metode på Array – den summerer opp alle tallene i arrayet. Til slutt bruker jeg list comprehension til å filtrere en range, og så kalle sum-metoden jeg lagde på resultatet.
CoffeeScript introduserer også en abstraksjon for klasse-basert objektorientering. Dette lar deg implementere klasser tilnærmet slik man gjør det i språk som f.eks. Ruby, og kompilatoren gjør dette om til prototype-basert JavaScript-kode.
Her følger klassen EulerProblem1 som har en kontruktør og to metoder. @limit og @sum er instansvariabler – den siste brukes til a cache resultatet av kalkuleringen. Selv om jeg sier calculate flere ganger vil selve kalkuleringen bare skje én gang slik jeg har valgt å gjøre det.
38 class EulerProblem1 39 constructor: (@limit) -> @sum = null 40 41 include: (n) -> n % 3 is 0 or n % 5 is 0 42 43 calculate: -> 44 if not @sum? 45 @sum += n for n in [1...@limit] when @include(n) 46 47 ep1 = new EulerProblem1 1000 48 do ep1.calculate 49 alert ep1.sum
I de tre siste linjene ser du hvordan jeg oppretter en instans av klassen, hvordan jeg kaller metoden calculate, og hvordan jeg henter ut summen.
De fleste av oss har et forhold til JavaScript. Dette forholdet er gjerne litt ambivalent. JavaScript blir viktigere og viktigere for å kunne tilfredstille de økte forventningene til dagens webløsninger, men JavaScript er et språk med mange svakheter. Da er det interessant at man ikke må kode i JavaScript direkte, men kan velge et renere og rikere språk som "kompileres" til JavaScript.
Det er viktig å poengtere at du kan benytte alle JavaScript-biblotekene du er vandt med også i CoffeeScript.
Er du Python- eller Ruby-utvikler er det nesten garantert at du kommer til å like CoffeeScript bedre enn JavaScript. Du bør uansett forsøke det, og så vurdere om det er verdt å legge denne ekstra abstraksjonen over JavaScript i akkurat ditt tilfelle.
Svaret på dette spørsmålet er desverre nei. Det finnes ikke enda (så vidt jeg vet) noen måte å debugge CoffeeScript på. Får man problemer vil man derfor få behov for å se på og debugge den genererte javaScript-koden. Koden er derimot helt grei, så dette er ikke et stort problem. Men man må kunne JavaScript!
Den offisielle CoffeeScript-siden finner du her. Den har en god primer som side-ved-side sammenligner CoffeeScript og JavaScript. Der kan du også teste språket direkte i browseren din.
Mye av det som finnes for å ta i bruk CoffeeScript er tilrettelagt for Linux og node.js-brukere. Hvis du er en Windows-bruker bør du ta en titt på CoffeeSharp, som gir deg det meste av hva du trenger.