Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
COFFEE SCRIPTRescuing JS from the accidents of its birth               truffles.me.uk                @timruffles
ESSENCE VS ACCIDENT
ESSENCE
ACCIDENT
ESSENCE: λvar BankAccount = function(balance) {var BankAccount = function(balance) {   balance || (balance = 0);   return ...
ACCIDENTvar BankAccount = function(balance) {   balance || (balance = 0);   return function(cmd,amount) {     amount || (a...
ESSENCEBankAccount = (balance = 0) ->  (cmd, amount = 0) ->    if cmd == "withdraw" then BankAccount balance - amount    e...
I’m sorry for picking so long a keyword                    Brendan Eich, Javascript’s Creator
It’s nice when whatyou are saying is clear
var winners = function(race) {    race || (race = "todays race");    return "Results of " + race ":n" +      [].slice.call...
Beautiful is better than ugly.Explicit is better than implicit.Simple is better than complex.Readability counts.There shou...
‘Idioms’ are often just missing      language features
// null guard                       possiblyNull?.foopossiblyNull && possiblyNull.foo;                                    ...
for (var key in anObject) {  if(anObject.hasOwnProperty(key) {    value = anObject[key];    console.log(key, value);  }}fo...
var transformed = [];for (var index=0, length = listOfThings.length; index < length; index++) {    transformed.push(transf...
var Widget = function(el) {                Widget = (@el) ->   this.sayHi = function() {                                  ...
It’s worse when nobody can       agree on them
var Animal = Actor.extend({  initialize: ...  override: function() {    this.__super__.override.apply(this,arguments)  }})...
You can do anything with function in    JS, but you shouldn’t have to.                            Brendan Eich
class Animal extends SuperClass  constructor: ->  override: ->    super_.extend Animal::, ai.instinctive
No mixins/traits
Why would you want == to        mean...?
""          ==   "0"         //   false        0           ==   ""          //   true        0           ==   "0"         ...
HALTING PROBLEMS   var values = {      foo: 1,      bar: 2,   };   values.foo = 3;   var maxSize = 2.pow(8);   var alerter...
EXPERIENCE
+-----------------------------------------------------+--------------+-------------------+----------+---------------+| Fil...
+------------------+--------------+-------------------+----------+---------------+|                  | Coffee Lines | Coff...
Looks like Ruby or Python,    isn’t a minefield:more people can write JS!
TOOLS$ guard$ coffee --watch app/coffee/**/*.coffee$ js2coffee legacy.js > new_hotness.coffee
HOW DOES IT WORK?                       ( )     Grammar                                JISONCoffee    Lexer     Rewriter  ...
TOKENS$ ./bin/coffee -te console.log "hello world"[IDENTIFIER console] [. .] [IDENTIFIER log] [CALL_START (][STRING "hello...
NODES$ ./bin/coffee -en console.log "hello world"Block  Call    Value "console"       Access "log"    Value ""hello world""
CODE$ ./bin/coffee -epb console.log "hello world"console.log("hello world");
How about extending coffee         script?
Let’s add <=> anduser-definable comparison        operators
GOALcoffee> 300 <=> 500-1coffee> arrogant = {">": -> true}{ >: [Function] }coffee> arrogant > Infinitytruecoffee> [1,4,2,3...
LEXER.COFFEEOPERATOR     = /// ^ (    ?: [-=]>            # function+    | <=>              # our new op     | [-+*/%<>&|^...
GRAMMAR.COFEE+   isComparison: ->+     @operator in [<, >, >=, <=, ===, !==, <=>]-     code = @first.compile(o, LEVEL_OP) ...
GRAMMAR.COFFEE+ comparison: -> """+   function(a,b,op) {+     if(!a || !b || !a[op]) {+       switch(op) {+         case "...
ALL TOGETHER NOW$ ./bin/coffee -et 5 <=> 6[NUMBER 5] [COMPARE <=>] [NUMBER 6] [TERMINATOR n]$ ./bin/coffee -en 5 <=> 6Bloc...
coffee> 300 <=> 500-1coffee> arrogant = {">": -> true}{ >: [Function] }coffee> arrogant > Infinitytruecoffee> [1,4,2,3,5,6...
Same power, 40% less code                                λ                                ;{                              ...
Thankstruffles.me.uk @timruffles
Upcoming SlideShare
Loading in …5
×

Coffeescript essence

442 views

Published on

Coffeescript is the essence of Javascript without the accident.

Published in: Technology, Business
  • Be the first to comment

  • Be the first to like this

Coffeescript essence

  1. 1. COFFEE SCRIPTRescuing JS from the accidents of its birth truffles.me.uk @timruffles
  2. 2. ESSENCE VS ACCIDENT
  3. 3. ESSENCE
  4. 4. ACCIDENT
  5. 5. ESSENCE: λvar BankAccount = function(balance) {var BankAccount = function(balance) { balance || (balance = 0); return function(cmd,amount) { return function(cmd,amount) { amount || (amount = 0); amount || (amount = 0); if(cmd == "withdraw") return BankAccount(balance - amount) if(cmd === "withdraw") return BankAccount(balance - amount) else if(cmd == "deposit") return BankAccount(balance + amount) else if(cmd === "deposit") return BankAccount(balance + amount) else if(cmd == "balance") return balance.toFixed(2) else if(cmd === "balance") return balance.toFixed(2) else return "What kind of person would try to " + cmd + else return "What kind of person would try to " + cmd + " a bank account?" " a bank account?" } }};};
  6. 6. ACCIDENTvar BankAccount = function(balance) { balance || (balance = 0); return function(cmd,amount) { amount || (amount = 0); if(cmd === "withdraw") return BankAccount(balance - amount); else if(cmd === "deposit") return BankAccount(balance + amount); else if(cmd === "balance") return balance.toFixed(2); else return "What kind of person would try to " + cmd + " a bank account?"; }};
  7. 7. ESSENCEBankAccount = (balance = 0) -> (cmd, amount = 0) -> if cmd == "withdraw" then BankAccount balance - amount else if cmd == "deposit" then BankAccount balance + amount else if cmd == "balance" then balance.toFixed 2 else "What kind of person would try to #{cmd} a bank account?"
  8. 8. I’m sorry for picking so long a keyword Brendan Eich, Javascript’s Creator
  9. 9. It’s nice when whatyou are saying is clear
  10. 10. var winners = function(race) { race || (race = "todays race"); return "Results of " + race ":n" + [].slice.call(arguments,1).join("n");};var keywordArgs = function(args) { foo = args.foo; bar = args.bar;};winners = (race = "todays race",winners...) -> "Results of #{race}:n#{ winners.join("n") }"keywordArgs = ({foo,bar}) ->
  11. 11. Beautiful is better than ugly.Explicit is better than implicit.Simple is better than complex.Readability counts.There should be one obvious way to do it. The Zen of Python, PEP: 20
  12. 12. ‘Idioms’ are often just missing language features
  13. 13. // null guard possiblyNull?.foopossiblyNull && possiblyNull.foo; valid = 5 < foo < 10// chained comparisonvar valid = foo > 5 && foo < 10; [@hidden,@displayed] = [@displayed,@hidden]wasDisplayed = this.displayed;this.displayed = this.hidden;this.hidden = wasDisplayed;
  14. 14. for (var key in anObject) { if(anObject.hasOwnProperty(key) { value = anObject[key]; console.log(key, value); }}for own key, value of anObject console.log key, value
  15. 15. var transformed = [];for (var index=0, length = listOfThings.length; index < length; index++) { transformed.push(transform(listOfThings[index]));};var squared = list.map(function(item) { return item * item; });transformed = (transform item for item in listOfThings)squared = (item * item for item in list)squared = list.map (item) -> item * item
  16. 16. var Widget = function(el) { Widget = (@el) -> this.sayHi = function() { @sayHi = => this.el.show() @el.show() }; @el.click @sayHi this.el = el; this.el.click(this.sayHi.bind(this));}; class Widget constructor: (@el) ->var Widget = function(el) { @el.click @sayHi self = this; sayHi: => this.sayHi = function() { @el.show() self.el.show() }; this.el = el; this.el.click(this.sayHi);};
  17. 17. It’s worse when nobody can agree on them
  18. 18. var Animal = Actor.extend({ initialize: ... override: function() { this.__super__.override.apply(this,arguments) }});_.extend Animal.prototype, ai.instinctive;dojo.provide("my.actors.Animal");dojo.declare("my.actors.Animal",[Actor,ai.instinctive],{ constructor: ... override: function() { this.inherited(arguments); }});var Animal = new JS.Class(Actor,{ init: ... override: function() { this.callSuper(arguments) }});Animal.include(ai.instinctive);
  19. 19. You can do anything with function in JS, but you shouldn’t have to. Brendan Eich
  20. 20. class Animal extends SuperClass constructor: -> override: -> super_.extend Animal::, ai.instinctive
  21. 21. No mixins/traits
  22. 22. Why would you want == to mean...?
  23. 23. "" == "0" // false 0 == "" // true 0 == "0" // true false == "false" // false false == "0" // true false == undefined // false false == null // false null == undefined // true " trn" == 0 // truehttp://bonsaiden.github.com/JavaScript-Garden/
  24. 24. HALTING PROBLEMS var values = { foo: 1, bar: 2, }; values.foo = 3; var maxSize = 2.pow(8); var alerter = function(value) { alert(value); } (5).times(function() { alerter("hello") }); var greeting = "hello" (10).times(function() { alert(greeting) });
  25. 25. EXPERIENCE
  26. 26. +-----------------------------------------------------+--------------+-------------------+----------+---------------+| File | Coffee Lines | Coffee Characters | JS Lines | JS Characters |+-----------------------------------------------------+--------------+-------------------+----------+---------------+| app/coffee/plv/accessors.coffee | 17 | 320 | x 1.9 | x 1.8 || app/coffee/plv/clock.coffee | 30 | 671 | x 1.6 | x 1.6 || app/coffee/plv/collection.coffee | 38 | 1089 | x 1.9 | x 1.4 || app/coffee/plv/comparison.coffee | 13 | 192 | x 1.5 | x 1.6 || app/coffee/plv/config.coffee | 24 | 597 | x 1.3 | x 1.1 || app/coffee/plv/console.coffee | 8 | 162 | x 1.9 | x 1.7 || app/coffee/plv/controller.coffee | 1 | 41 | x 3.0 | x 1.5 || app/coffee/plv/controllers/main.coffee | 12 | 371 | x 1.8 | x 1.2 || app/coffee/plv/ext/backbone.coffee | 16 | 514 | x 1.9 | x 1.6 || app/coffee/plv/ext/class.coffee | 10 | 288 | x 3.3 | x 2.0 || app/coffee/plv/ext/core.coffee | 126 | 2178 | x 1.7 | x 1.6 || app/coffee/plv/ext/date.coffee | 57 | 1206 | x 1.8 | x 1.5 || app/coffee/plv/layers/core.coffee | 1 | 67 | x 1.0 | x 1.0 || app/coffee/plv/model.coffee | 115 | 3194 | x 1.9 | x 1.5 |....| app/coffee/plv/models/entry_handler.coffee | 10 | 302 | x 1.9 | x 1.2 || app/coffee/plv/models/event.coffee | 15 | 399 | x 1.8 | x 1.6 || app/coffee/plv/models/football/point_in_game.coffee | 113 | 2262 | x 1.5 | x 1.4 || app/coffee/plv/models/football.coffee | 35 | 1272 | x 1.9 | x 1.4 || app/coffee/plv/models/game.coffee | 2 | 71 | x 1.5 | x 1.3 |...| app/coffee/plv/polling.coffee | 30 | 560 | x 1.9 | x 1.8 || app/coffee/plv/procs/boot.coffee | 40 | 1000 | x 1.3 | x 1.1 || app/coffee/plv/share.coffee | 15 | 702 | x 2.1 | x 1.2 || app/coffee/plv/spec/factory.coffee | 194 | 4686 | x 1.1 | x 1.2 || app/coffee/plv/spec/fake_game.coffee | 211 | 5742 | x 1.7 | x 1.2 || app/coffee/plv/spec/fake_server.coffee | 44 | 2360 | x 2.2 | x 1.3 || app/coffee/plv/sync.coffee | 12 | 352 | x 2.0 | x 1.3 || app/coffee/plv/ui.coffee | 42 | 903 | x 1.6 | x 1.7 || app/coffee/plv/view.coffee | 9 | 299 | x 1.6 | x 1.3 || app/coffee/plv/views/avatar_picker.coffee | 12 | 385 | x 1.6 | x 1.4 || app/coffee/plv/views/backup_players.coffee | 5 | 142 | x 1.6 | x 1.3 || app/coffee/plv/views/backup_toggle.coffee | 25 | 729 | x 1.4 | x 1.6 || app/coffee/plv/views/big_game.coffee | 71 | 2623 | x 1.3 | x 1.3 |...| app/coffee/plv/views/loading.coffee | 42 | 1067 | x 1.0 | x 1.3 || app/coffee/plv/views/main.coffee | 336 | 9290 | x 1.3 | x 1.3 || app/coffee/plv/views/message.coffee | 39 | 954 | x 1.8 | x 1.6 || app/coffee/plv/views/nav.coffee | 44 | 931 | x 1.4 | x 1.3 |....| app/coffee/plv/views/sounds.coffee | 85 | 2239 | x 1.5 | x 1.4 || app/coffee/plv/views/standings.coffee | 78 | 2501 | x 1.5 | x 1.4 || app/coffee/plv/views/tutorial.coffee | 81 | 2817 | x 1.7 | x 1.4 || app/coffee/plv/web.coffee | 18 | 461 | x 2.7 | x 2.2 |+-----------------------------------------------------+--------------+-------------------+----------+---------------+| Total | 3552 | 98736 | 5563 | 134774 || Diff | | | + 2011 | + 36038 |+-----------------------------------------------------+--------------+-------------------+----------+---------------+| Ratios JS/Coffee | | | x 1.566 | x 1.365 |+-----------------------------------------------------+--------------+-------------------+----------+---------------+
  27. 27. +------------------+--------------+-------------------+----------+---------------+| | Coffee Lines | Coffee Characters | JS Lines | JS Characters |+------------------+--------------+-------------------+----------+---------------+| Total | 3552 | 98736 | 5563 | 134774 || Diff | | | + 2011 | + 36038 |+------------------+--------------+-------------------+----------+---------------+| Ratios JS/Coffee | | | x 1.566 | x 1.365 |+------------------+--------------+-------------------+----------+---------------+
  28. 28. Looks like Ruby or Python, isn’t a minefield:more people can write JS!
  29. 29. TOOLS$ guard$ coffee --watch app/coffee/**/*.coffee$ js2coffee legacy.js > new_hotness.coffee
  30. 30. HOW DOES IT WORK? ( ) Grammar JISONCoffee Lexer Rewriter Parser JS Code Tokens Tokens Nodes Code
  31. 31. TOKENS$ ./bin/coffee -te console.log "hello world"[IDENTIFIER console] [. .] [IDENTIFIER log] [CALL_START (][STRING "hello world"] [CALL_END )] [TERMINATOR n]
  32. 32. NODES$ ./bin/coffee -en console.log "hello world"Block Call Value "console" Access "log" Value ""hello world""
  33. 33. CODE$ ./bin/coffee -epb console.log "hello world"console.log("hello world");
  34. 34. How about extending coffee script?
  35. 35. Let’s add <=> anduser-definable comparison operators
  36. 36. GOALcoffee> 300 <=> 500-1coffee> arrogant = {">": -> true}{ >: [Function] }coffee> arrogant > Infinitytruecoffee> [1,4,2,3,5,6,7].map (val) -> val <=> 5[ -1, -1, -1, -1, 0, 1, 1 ]
  37. 37. LEXER.COFFEEOPERATOR = /// ^ ( ?: [-=]> # function+ | <=> # our new op | [-+*/%<>&|^!?=]= # compound assign / compare... # Comparison tokens.-COMPARE = [==, !=, <, >, <=, >=]+COMPARE = [<=>, ==, !=, <, >, <=, >=]
  38. 38. GRAMMAR.COFEE+ isComparison: ->+ @operator in [<, >, >=, <=, ===, !==, <=>]- code = @first.compile(o, LEVEL_OP) + + @operator + +- @second.compile(o, LEVEL_OP)+ code = if @isComparison()+ utility("comparison") + "(" + @first.compile(o, LEVEL_OP) + ", "++ @second.compile(o, LEVEL_OP) + ", #{@operator})"+ else+ @first.compile(o, LEVEL_OP) + + @operator + ++ @second.compile(o, LEVEL_OP) if o.level <= LEVEL_OP then code else "(#{code})"
  39. 39. GRAMMAR.COFFEE+ comparison: -> """+ function(a,b,op) {+ if(!a || !b || !a[op]) {+ switch(op) {+ case "===":+ return a === b;+ case ">=":...+ case "<=>":+ if(a === b) return 0;+ return a > b ? 1 : -1;+ default:+ throw "Unknown operator: " + op;+ }+ } else {+ return a[op](b);+ }+ }+ """
  40. 40. ALL TOGETHER NOW$ ./bin/coffee -et 5 <=> 6[NUMBER 5] [COMPARE <=>] [NUMBER 6] [TERMINATOR n]$ ./bin/coffee -en 5 <=> 6Block Op <=> Value "5" Value "6"$ ./bin/coffee -epb 5 <=> 6var __comparison = function(a,b,op) { ...};__comparison(5, 6, <=>);
  41. 41. coffee> 300 <=> 500-1coffee> arrogant = {">": -> true}{ >: [Function] }coffee> arrogant > Infinitytruecoffee> [1,4,2,3,5,6,7].map (val) -> val <=> 5[ -1, -1, -1, -1, 0, 1, 1 ]
  42. 42. Same power, 40% less code λ ;{ λLess noise {foo:bar,}Less ?!?! "n" == 0Familiar, clean syntaxNo need to wait for IE(took 6.3 years for JS 1.5)If you fancy it, add your ownlanguage features
  43. 43. Thankstruffles.me.uk @timruffles

×