Sunday, September 23rd, 2012
Skriv en kommentar

JavaScript er som alle vet et viktig språk, men syntax-messig har det sine utfordringer. For godt over et år siden fant jeg det populære språket CoffeeScript, som kompileres ned til JavaScript. Det vil si at det i praksis kan erstatte JavaScript-utvikling.

Men det finnes mange slike språk som forsøker å gjøre JavaScript-koding mindre smertefullt. I denne blogposten har jeg tatt litt JavaScript og kodet det samme i tre slike språk: CoffeeScript, LispyScript og PogoScript. Alle disse språkene er plugins til node.js, og er enkle å komme igang med. Målet var å illustrere de ulike syntaksene, og se om noen av dem er “bedre” enn andre.

Bowling i JavaScript

Koden er en løsning for å finne poengscoren for en bowlingserie. Serien er representert som et array av kast – dvs. av hvor mange sjegler som ble felt av kastet. Løsningen definerer to funksjoner: en som finner poengscoren til en frame (omgang), og en som looper over hele spillet og samler opp totalscoren. Spill-arrayet “spises opp” underveis i denne løsningen – dette er altså ikke funksjonell programmering, her muterer jeg rått og hemningsløst.

var frameScore = function(rolls) {
  if(rolls[0] === 10)
    return rolls.shift() + rolls[0] + rolls[1];
  if(rolls[0] + rolls[1] === 10)
    return rolls.shift() + rolls.shift() + rolls[0];
  return rolls.shift() + rolls.shift();
};

var score = function(rolls) {
  var score = 0;
  for(var i = 0; i < 10; i++)
    score += frameScore(rolls);
  return score;
};

var gameZeros = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    gameRandom = [10,8,2,5,5,7,3,10,10,7,1,10,10,10,10,8],
    gamePerfect = [10,10,10,10,10,10,10,10,10,10,10,10];

console.log(score(gameZeros));    // => 0
console.log(score(gameRandom));   // => 213
console.log(score(gamePerfect));  // => 300

I de siste linjene definerer jeg tre test-omganger, beregner poengsummen, og skriver den ut.

Disclamer: Koden er en modifisert utgave av Karl Westin’s Bowling Game Kata-løsning som man kan finne på Github.

Bowling i LispyScript

Det finnes flere Lisp-varianter som kompilerer til JavaScript. Den mest kjente er kanskje ClojureScript. Et annet heter Sibilant, og det ser veldig egentlig veldig bra ut. Men jeg har valgt språket LispyScript, som i alle fall for øyeblikket er en veldig enkel Lispifisering av JavaScript.

Igjen har jeg de to samme funksjonene – jeg har forsøkt å løse oppgaven så likt som den orginale løsningen som mulig.

(var frameScore
  (function (rolls)
     (if (= 10 rolls[0])
       (+ (rolls.shift) (+ rolls[0] rolls[1]))
       (if (= 10 (+ rolls[0] rolls[1]))
         (+ (rolls.shift) (+ (rolls.shift) rolls[0]))
         (+ (rolls.shift) (rolls.shift))))))

(var score
  (function (rolls)
     (loop (i score) (0 0)
       (if (< i 10)
         (recur ++i (+ score (frameScore rolls)))
         score))))

(var gameZeros [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
(var gameRandom [10,8,2,5,5,7,3,10,10,7,1,10,10,10,10,8])
(var gamePerfect [10,10,10,10,10,10,10,10,10,10,10,10])

(console.log (score gameZeros))  ; => 0
(console.log (score gameRandom)) ; => 213
(console.log (score gamePerfect)); => 300

En utfordring i forhold til denne oppgaven er at LispyScript ikke har noen normal for-løkke. Jeg valgte å bruke en rekursiv loop-makro med en teller (i) som jeg inkrementerer og kontrollerer underveis i score-funksjonen. At pluss-funksjonen bare kunne ta to argumenter gjorde også at frameScore ble noe mindre oversiktelig enn jeg hadde håpet.

Konklusjon: Jeg er glad i Lisp, og kan tenke meg å bruke det for JavaScript-utvikling – men for oppgaver som denne kom LispyScript til kort. Språket trenger flere utvidelser (makoer) for å bli elegant, men bare det at det i det hele tatt finnes makro-støtte gjør det hele interessant.

Bowling i CoffeeScript

CoffeeScript har jeg vist frem før, og jeg liker det veldig godt. Spesielt er det  kraftfullt med sine list comprehensions – noe som gjorde score-funksjonen du ser nedenfor ganske elegant og kompakt (noen vil kanskje si kryptisk, men man kan ikke gjøre alle fornøyde). Indenteringsreglene i CoffeeScript gjør også koden enklere å skrive enn både JavaScript og LispyScript-variantene.

scoreFrame = (rolls) ->
  if rolls[0] == 10
    rolls.shift() + rolls[0] + rolls[1]
  else if rolls[0] + rolls[1] == 10
    rolls.shift() + rolls.shift() + rolls[0]
  else
    rolls.shift() + rolls.shift()

score = (rolls) ->
  (scoreFrame rolls for _ in [0..9])
    .reduce (t, s) -> t + s

gameZeros = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
gameRandom = [10,8,2,5,5,7,3,10,10,7,1,10,10,10,10,8]
gamePerfect = [10,10,10,10,10,10,10,10,10,10,10,10]

console.log score gameZeros    # => 0
console.log score gameRandom   # => 213
console.log score gamePerfect  # => 300

Konklusjon: Fortsatt den mest realistiske JavaScript-erstatningen. Koden er fin, men vil føles fremmed for dem som bare har erfaring med C-lignende språk.

Bowling i PogoScript

Jeg avslutter med det som kanskje er det mest interessante av disse språkene; PogoScript er et språk som legger ekstra vekt på lesbarhet, og som kan være veldig egnet til å skrive domenespesifike språk. Dette kommer nok ikke så godt frem overalt i koden min, men du kan se noen interessante ting som for eksempel at variabelnavn kan inneholde space. Funksjonsnavn inneholder også sine parametre (i paranteser), og disse kan plasseres hvor som helst i navnet. Dermed kan du lage funksjoner som heter ting som for eksempel “move the file (filename) to (directory)”. Noe lignende har jeg aldri sett i noe annet språk.

score of next frame in (rolls) =
    if (rolls.0 == 10)
        rolls.shift() + rolls.0 + rolls.1
    else if ((rolls.0 + rolls.1) == 10)
        rolls.shift() + rolls.shift() + rolls.0
    else
        rolls.shift() + rolls.shift()

score (rolls) =
    result = 0
    for (i = 0, i < 10, i = i + 1)
        result = result + score of next frame in (rolls)

    result

game zeros = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
game random = [10,8,2,5,5,7,3,10,10,7,1,10,10,10,10,8]
game perfect = [10,10,10,10,10,10,10,10,10,10,10,10]

console.log (score (game zeros))   // => 0
console.log (score (game random))  // => 213
console.log (score (game perfect)) // => 300

Slik jeg forstår det er PogoScript veldig utvidbart; indentering definerer en ny kodeblokk, og denne eksekveres av funksjonen kodeblokken sendes til. Hvis du har erfaring fra Lisp, Rebol, Ruby eller Io skjønner du kanskje hva jeg mener. I praksis betyr dette at språket har markoer. For-løkken i score-funksjonen er til eksempel egentlig ingen for-løkke i vanlig forstand, men en funksjon som heter for.

Konklusjon: PogoScript er nok ikke et språk jeg ville valgt som en erstatter for JavaScript. Men det er et interessant språk å studere nærmere, og det kan være aktuelt om man en elle rannen gang har behov for å skrive ekstremt lesbar kode.

Resultatet

  • Språket jeg synes er vakrest å se på: LispyScript
  • Språket som var mest interessant: PogoScript
  • Språket som var enklest til denne oppgaven: CoffeeScript
  • Grunnen til at Gud og Hverman lager alle disse språkene: JavaScript

Kategorier: Polyglot, Webutvikling.
RSS feed for kommentarene. Tilbaketråkk.

3 kommentarer til “JavaCoffeeLispyPogoScript”

  1. Mer eksperimentering med PogoScript Says:

    [...] JavaCoffeeLispyPogoScript [...]

  2. Torbjørn Says:

    Det har gått opp for meg at det finnes en hel haug, sikkert hundrevis av kompilatorer som tar kode skrevet i et eller annet språk og gjør det om til JavaScript. Vil du skrive web- eller node-koden din i Ruby, Python, F#, Forth eller Scheme? Ikke noe problem – det finnes en kompilator for alle. CoffeeScript’s wiki har en liste over språk, og altJS er en annen, god kilde. Mye å leke seg med her…

  3. Lister av Lambda Says:

    [...] introduserte mine lesere for LispyScript første gang i posten JavaCoffeeLispyPogoScript i fjor høst. LispyScript er en Lisp-variant som kan brukes sammen med node.js, og som produserer [...]

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Siste kommentarer

Torbjørn
PS: Takk til Børge Hansen, som delte SCARF-modellen med meg!...
Børge Hansen
Denne likte jeg veldig godt. Du skriver godt og har gode betraktninger  Keep it up – flere trenger å tørre å lære mer om ledelse – du l...
Tormod
Er egentlig ikke overrasket. F# sin fortè er programmererens produktivitet/kvalitet og anledning til parallell kjøring. Men kjøremotoren har ...
Stian
Ville også prøvd med et større problem (x100 eller x1000 f.eks). Når man snakker så små brøkdeler av et sekund som her så kan tiden for en ell...
Torbjørn
Har ikke sjekket - tar en titt i morgen hvis tid :)...
Einar W. Høst
Mhp tco: hva sier ILSpy?...
Torbjørn
Har ikke sett noe på PSeq før, men kjenner til den typen funksjoner fra blant annet Clojure. Og problemet med slike funksjoner i sammenhenger som de...
Håvard
Veldig bra sammenligning! Har du sett på ytelsen av PSeq.* fra powerpakken? Tipper den vil gi performancehit på små mengder, men kan kanskje resul...
Torbjørn
Jeg kom på en demonstrasjon-variant til jeg burde inkludere, nemlig bruk av list comprehension (en type computation expression (også kalt monads)). ...
Einar W. Høst
Interessant, det blir en trade-off mellom eleganse og fart på en måte. Den funksjonelle løsningen med vanlig filter er ren og pen, mens den imperat...
Creative Commons-lisens
Innholdet på denne bloggen er tilgjengelig under Creative Commons Navngivelse-Ikkekommersiell-DelPåSammeVilkår 3.0 Norge lisens.

Programmeringsbloggen
Kjempekjekt.com

© 2006-2013 Torbjørn Marø

Jeg har vært en profesjonell programmerer siden 1999, og dette er min blogg. Målet med bloggen er å stimulere meg selv og alle andre til kontinuerlig eksperimentering og læring.

Jeg forsøker å være allsidig, og programmerer blant annet i C#, Ruby, Erlang og Clojure.

Jeg praktiserer TDD og andre smidige utviklingspraksiser. Jeg er opptatt av kvalitet og ren kode.

Dette og ganske mye mer kan du lese om på denne bloggen!