# You Don't Know Beans # About CoffeeScript ## (This'll Give You A Taste) ##   #### Aseem Kishore / June 2013
[![](/images/neo4j-lessons-learned/fiftythree-hp.png)](http://www.fiftythree.com/)
[![](/images/neo4j-lessons-learned/thingdom-hp-gasi.png)](http://www.thethingdom.com/)
[![](/images/neo4j-lessons-learned/zoomit-hp.png)](http://zoom.it/)
  ``` class Person constructor: (@name) -> toString: -> @name class Employee extends Person constructor: (name, @department) -> super name toString: -> "#{@name} in #{@department}" alice = new Employee 'Alice Jenkins', 'Engineering' bob = new Employee 'Bob Spoth', 'Marketing' console.log "Our employees are #{alice} and #{bob}." ```
``` var Employee, Person, alice, bob, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Person = (function() { function Person(name) { this.name = name; } Person.prototype.toString = function() { return this.name; }; return Person; })(); Employee = (function(_super) { __extends(Employee, _super); function Employee(name, department) { this.department = department; Employee.__super__.constructor.call(this, name); } Employee.prototype.toString = function() { return "" + this.name + " in " + this.department; }; return Employee; })(Person); alice = new Employee('Alice Jenkins', 'Engineering'); bob = new Employee('Bob Spoth', 'Marketing'); console.log("Our employees are " + alice + " and " + bob + "."); ```
# CoffeeScript is...
  • "Just" JavaScript
  • A strict subset of JS
  • A @jashkenas production
  • Open-source, active, stable
# CoffeeScript is... ![The 10th most popular language on GitHub!](/images/coffeescript-talk/github-langs-coffeescript.png)
# Why
  • Syntax
  • Convenience
  • Productivity
  • Robustness
  • ES6
# Why Not
  • Syntax
  • Abstraction
  • Interop
  • Compilation
  • Debugging
![O RLY?](/images/coffeescript-talk/orly-owl.jpg)
# Mythbusters
  • (Syntax)
  • 1:1 semantics
  • Same APIs
  • Many integrations
  • Source maps
# Syntax  
sum = (a, b) ->
    a + b

x = 5
y = 10

alert sum x, y
var sum, x, y;

sum = function(a, b) {
  return a + b;
};

x = 5;
y = 10;

alert(sum(x, y));
# Syntax - Significant whitespace - Arrows for functions - Implicit parentheses - Implicit returns - No vars
  ![Red pill, blue pill](/images/coffeescript-talk/matrix-red-pill-blue-pill.jpg)
# Aside  
// Why this?

var sum;

sum = function(a, b) {
  return a + b;
};
// And not this?

function sum(a, b) {
  return a + b;
}
# Aside  
Originally every function that could have a sensible name retrieved for it was given one, but IE versions 8 and down have scoping issues where the named function is treated as both a declaration and an expression. See this for more information.
  — [CoffeeScript FAQ](https://github.com/jashkenas/coffee-script/wiki/FAQ#functions)
![Robust!](/images/coffeescript-talk/robust-coffee-lounge.jpg)
# Differences  
if not found
    lost()

if father instanceof Asian and
   grade isnt 'A'
    disappoint()
if (!found) {
  lost();
}

if (father instanceof Asian && grade !== 'A') {
  disappoint();
}
  ![High expectation Asian father meme](/images/coffeescript-talk/asian-dad-meme.jpg)
# Differences  
switch grade
    when 'A', 'B', 'C'
        pass()
    when 'D'
        warn()
        pass()
    else
        fail()
switch (grade) {
  case 'A':
  case 'B':
  case 'C':
    pass();
    break;
  case 'D':
    warn();
    pass();
    break;
  default:
    fail();
}
# Differences  
if elmt in array
    alert 'found!'

if key of obj
    alert 'found!'
var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

if (__indexOf.call(array, elmt) >= 0) {
  alert('found!');
}

if (key in obj) {
  alert('found!');
}
# Differences  
for elmt in array
    console.log elmt

for key, val of obj
    console.log key, val
var elmt, key, val, _i, _len;

for (_i = 0, _len = array.length; _i < _len; _i++) {
  elmt = array[_i];
  console.log(elmt);
}

for (key in obj) {
  val = obj[key];
  console.log(key, val);
}
![Danger](/images/coffeescript-talk/danger.jpg)
#   # Let's talk... # Conveniences
# Iterate
for elmt, i in array
    console.log i, elmt

for own key, val of obj
    console.log key, val
var elmt, i, key, val, _i, _len,
  __hasProp = {}.hasOwnProperty;

for (i = _i = 0, _len = array.length; _i < _len; i = ++_i) {
  elmt = array[i];
  console.log(i, elmt);
}

for (key in obj) {
  if (!__hasProp.call(obj, key)) continue;
  val = obj[key];
  console.log(key, val);
}
# Commas Optional
user =
    name: 'John Smith'
    username: 'john'

    ids:
        facebook: 1234
        twitter: 5678
        google: 9012

    emails: [
        'john@smith.com'
        'jsmith@acme.com'
        'john.smith@gmail.com'
    ]
var user;

user = {
  name: 'John Smith',
  username: 'john',
  ids: {
    facebook: 1234,
    twitter: 5678,
    google: 9012
  },
  emails: ['john@smith.com', 'jsmith@acme.com', 'john.smith@gmail.com']
};
# Chain Them 'Rators
if width <= 320
    mobilePortrait()

else if 321 <= width <= 480
    mobileLandscape()

else if 481 <= width <= 768
    tabletPortrait()

else if 769 <= width <= 1024
    tabletLandscape()

else
    desktop()
if (width <= 320) {
  mobilePortrait();
} else if ((321 <= width && width <= 480)) {
  mobileLandscape();
} else if ((481 <= width && width <= 768)) {
  tabletPortrait();
} else if ((769 <= width && width <= 1024)) {
  tabletLandscape();
} else {
  desktop();
}
# Where You @  
$('a').on 'click', ->
    trackLink @href
$('a').on('click', function() {
  return trackLink(this.href);
});
# You So Fat  
$('a').on 'click', (evt) ->
    trackLink @href
    evt.preventDefault()

    # suck it, users:
    setTimeout =>
        window.open @href
    , 1
$('a').on('click', function(evt) {
  var _this = this;

  trackLink(this.href);
  evt.preventDefault();

  return setTimeout(function() {
    return window.open(_this.href);
  }, 1);
});
# Do (Be Do Be Do)  
do ->
    willy = 'nilly'

# alerts 'undefined'
alert typeof willy
(function() {
  var willy;
  return willy = 'nilly';
})();

alert(typeof willy);
# Do (Be Do Be Do)  
results = {}

for url in urls
    $.get url, (data) ->
        results[url] = data

# oops, closure problem!
var results, url, _i, _len;

results = {};

for (_i = 0, _len = urls.length; _i < _len; _i++) {
  url = urls[_i];
  $.get(url, function(data) {
    return results[url] = data;
  });
}
# Do (Be Do Be Do)  
results = {}

for url in urls
    do (url) ->
        $.get url, (data) ->
            results[url] = data
var results, url, _fn, _i, _len;

results = {};

_fn = function(url) {
  return $.get(url, function(data) {
    return results[url] = data;
  });
};

for (_i = 0, _len = urls.length; _i < _len; _i++) {
  url = urls[_i];
  _fn(url);
}
# Do (Be Do Be Do)  
numTimeouts = 0

do (orig=setTimeout) ->
    setTimeout = (func, ms) ->
        numTimeouts++
        orig func, ms
var numTimeouts;

numTimeouts = 0;

(function(orig) {
  var setTimeout;
  return setTimeout = function(func, ms) {
    numTimeouts++;
    return orig(func, ms);
  };
})(setTimeout);
# Destructure Me, Bro  
[x, y] = coords

{count, offset} = opts
var count, offset, x, y;

x = coords[0], y = coords[1];

count = opts.count, offset = opts.offset;
# Restructure Me, Bro  
res.render 'profile', {
    user
    photos
    csrfToken: req._csrf
}
res.render('profile', {
  user: user,
  photos: photos,
  csrfToken: req._csrf
});
# Both Together  
lookup = (lat, lng) ->
    # ...
    return [city, state]

for {text, geo} in tweets
    [lat, lng] = geo
    [city] = lookup lat, lng
var city, geo, lat, lng, lookup, text, _i, _len, _ref;

lookup = function(lat, lng) {
  return [city, state];
};

for (_i = 0, _len = tweets.length; _i < _len; _i++) {
  _ref = tweets[_i], text = _ref.text, geo = _ref.geo;
  lat = geo[0], lng = geo[1];
  city = lookup(lat, lng)[0];
}
# Existential Crisis  
if obj?
    obj.id = getNextId()
if (typeof obj !== "undefined" && obj !== null) {
  obj.id = getNextId();
}
# Existential Crisis  
setProp = (key, val) ->
    if val?
        _props[key] = val
    else
        delete _props[key]
var setProp;

setProp = function(key, val) {
  if (val != null) {
    return _props[key] = val;
  } else {
    return delete _props[key];
  }
};
# Existential Crisis  
payToll = (numAxes) ->
    numAxes = numAxes ? 2
    return 0.25 * numAxes

payToll()   # 0.50
payToll 0   # 0
var payToll;

payToll = function(numAxes) {
  numAxes = numAxes != null ? numAxes : 2;
  return 0.25 * numAxes;
};
# Existential Crisis  
payToll = (numAxes) ->
    numAxes ?= 2
    return 0.25 * numAxes

payToll()   # 0.50
payToll 0   # 0
var payToll;

payToll = function(numAxes) {
  if (numAxes == null) {
    numAxes = 2;
  }
  return 0.25 * numAxes;
};
# When In Greece  
payToll = (numAxes=2) ->
    return 0.25 * numAxes

payToll()   # 0.50
payToll 0   # 0
var payToll;

payToll = function(numAxes) {
  if (numAxes == null) {
    numAxes = 2;
  }
  return 0.25 * numAxes;
};
# When In Greece  
fetch = (opts={}) ->
    {count, offset} = opts
    count or= 10
    offset or= 0

    # ...
var fetch;

fetch = function(opts) {
  var count, offset;
  if (opts == null) {
    opts = {};
  }
  count = opts.count, offset = opts.offset;
  count || (count = 10);
  offset || (offset = 0);

  // ...
};
# Dry Things Up...  
if console? and console.log?
    console.log 'enabled'
if ((typeof console !== "undefined" && console !== null) && (console.log != null)) {
  console.log('enabled');
}
# Like A Sponge  
console?.log? 'enabled'
if (typeof console !== "undefined" && console !== null) {
  if (typeof console.log === "function") {
    console.log('enabled');
  }
}
# Like A Sponge  
sync = (callback) ->
    # ...
    callback? results
var sync;

sync = function(callback) {
  return typeof callback === "function" ? callback(results) : void 0;
};
# Like A Sponge  
items?[0]
if (typeof items !== "undefined" && items !== null) {
  items[0];
}
# Interpol Action  
"#{name} is #{age} years old."
"" + name + " is " + age + " years old."
# Interpol Action  
"/api/#{users[0].id}/info"
"/api/" + users[0].id + "/info"
# Line 'Em Up  
$(user).html """
  <a href="#{user.url}">
    <img alt="#{user.name}"
      src="#{user.avatar}">
  </a>
"""
$(user).html("<a href=\"" + user.url + "\">\n    <img alt=\"" + user.name + "\"\n        src=\"" + user.avatar + "\">\n</a>");
# Irregular Expressions  
url.match ///
    # absolute; SSL optional:
    https?://

    # simplified domain:
    [a-zA-Z0-9-.]+

    # simplified path:
    /.*
///
url.match(/https?:\/\/[a-zA-Z0-9-.]+\/.*/);
#   ![Sugar, but looks like cocaine](/images/coffeescript-talk/sugar-cocaine.jpg)
  ![Sugar cocaine police incident](/images/coffeescript-talk/sugar-cocaine-incident.jpg)
#   # More... # Power...
###   ![Everything is an expression](/images/coffeescript-talk/everything-is-an-expression-meme.jpg)
# The Ternary Point  
view =
    if loggedIn
        'home'
    else
        'landing'
var view;

view = loggedIn ? 'home' : 'landing';
# The Ternary Point  
view = (if loggedIn then 'home' else 'landing')
var view;

view = loggedIn ? 'home' : 'landing';
# The 'Ol Switcharoo
func = switch req.method
    when 'GET'
        'show'
    when 'POST', 'PATCH'
        'update'
    when 'PUT'
        'replace'
    when 'DELETE'
        'destroy'
    else
        'handle'
var func;

func = (function() {
  switch (req.method) {
    case 'GET':
      return 'show';
    case 'POST':
    case 'PATCH':
      return 'update';
    case 'PUT':
      return 'replace';
    case 'DELETE':
      return 'destroy';
    default:
      return 'handle';
  }
})();
# Try It Out  
result =
    try
        readFromCache()
    catch
        generate()
var result;

result = (function() {
  try {
    return readFromCache();
  } catch (_error) {
    return generate();
  }
})();
# Try It Out  
result = try
    readFromCache()

result or= generate()
var result;

result = (function() {
  try {
    return readFromCache();
  } catch (_error) {}
})();

result || (result = generate());
# Map It Out  
names =
    for user in users
        user.name
var names, user;

names = (function() {
  var _i, _len, _results;
  _results = [];
  for (_i = 0, _len = users.length; _i < _len; _i++) {
    user = users[_i];
    _results.push(user.name);
  }
  return _results;
})();
# Instagram It  
names =
  for u in users when u.isAdmin
    u.name
var names, u;

names = (function() {
  var _i, _len, _results;
  _results = [];
  for (_i = 0, _len = users.length; _i < _len; _i++) {
    u = users[_i];
    if (u.isAdmin) {
      _results.push(u.name);
    }
  }
  return _results;
})();
# Comprehend Me?  
names = (
  user.name for user in users
)
var names, user;

names = (function() {
  var _i, _len, _results;
  _results = [];
  for (_i = 0, _len = users.length; _i < _len; _i++) {
    user = users[_i];
    _results.push(user.name);
  }
  return _results;
})();
# Watch the Order  
names = u.name for u in users

# equivalent to:

for u in users
    names = u.name
var names, u, _i, _len;

for (_i = 0, _len = users.length; _i < _len; _i++) {
  u = users[_i];
  names = u.name;
}
# Reverse Maps  
map = {}
map[u.id] = u for u in users
var map, u, _i, _len;

map = {};

for (_i = 0, _len = users.length; _i < _len; _i++) {
  u = users[_i];
  map[u.id] = u;
}
# Ready For More?  
#   # Time To Get # Classy
# Prototypical  
function Person(name) {
    this.name = name;
}

Person.prototype.toString = function () {
    return this.name;
};
# A Little Classier
class Person

    constructor: (@name) ->

    toString: ->
        @name
var Person;

Person = (function() {
  function Person(name) {
    this.name = name;
  }

  Person.prototype.toString = function() {
    return this.name;
  };

  return Person;

})();
# Prototypical
function Person(name) {
    this.name = name;
}

Person.prototype.toString = function () {
    return this.name;
};

function Employee(name, dept) {
    Person.call(this, name);
    this.dept = dept;
}

Employee.prototype = new Person();

Employee.prototype.toString = function () {
    return Person.prototype.toString.call(this) +
        ' (' + this.dept + ')';
};
# A Little Classier
class Person

  constructor: (@name) ->

  toString: ->
    @name

class Employee extends Person

  constructor: (name, @dept) ->
    super name

  toString: ->
    "#{super} (#{@dept})"
var Employee, Person,
  __hasProp = {}.hasOwnProperty,
  __extends = // ...

Person = (function() {
  function Person(name) {
    this.name = name;
  }

  Person.prototype.toString = function() {
    return this.name;
  };

  return Person;

})();

Employee = (function(_super) {
  __extends(Employee, _super);

  function Employee(name, dept) {
    this.dept = dept;
    Employee.__super__.constructor.call(this, name);
  }

  Employee.prototype.toString = function() {
    return "" + Employee.__super__.toString.apply(this, arguments) + " (" + this.dept + ")";
  };

  return Employee;

})(Person);
# Extend Yourself
var __extends = function(child, parent) {

    for (var key in parent) {
        if (__hasProp.call(parent, key))
            child[key] = parent[key];
    }

    function ctor() {
        this.constructor = child;
    }

    ctor.prototype = parent.prototype;
    child.prototype = new ctor();
    child.__super__ = parent.prototype;

    return child;

};
# Statics & Dynamics
class Model
  @TYPE: null
  constructor: (@elmt) ->
    @type = @constructor.TYPE

class User extends Model
  @TYPE: 'user'

class Photo extends Model
  @TYPE: 'photo'
var // ...

Model = (function() {
  Model.TYPE = null;

  function Model(elmt) {
    this.elmt = elmt;
    this.type = this.constructor.TYPE;
  }

  return Model;

})();

User = (function(_super) {
  __extends(User, _super);

  function User() {
    _ref = User.__super__.constructor.apply(this, arguments);
    return _ref;
  }

  User.TYPE = 'user';

  return User;

})(Model);

Photo = // ...
# Statics & Dynamics
class Model
  @TYPE: null
  constructor: (@elmt) ->
    @type = @constructor.TYPE

  @all: ->
    if @TYPE
      db.lookup 'type', @TYPE
    else
      db.all()

class User extends Model
  @TYPE: 'user'

class Photo extends Model
  @TYPE: 'photo'
var // ...

Model = (function() {
  Model.TYPE = null;

  function Model(elmt) {
    this.elmt = elmt;
    this.type = this.constructor.TYPE;
  }

  Model.all = function() {
    if (this.TYPE) {
      return db.lookup('type', this.TYPE);
    } else {
      return db.all();
    }
  };

  return Model;

})();

User = // ...

Photo = // ...
# Executive Branch
class Model
  @_attrs: null
  @_attr: (name, type) ->
    @_attrs or= {}
    @_attrs[name] = type
    @::[name] = null

class User extends Model
  @_attr 'name', String
  @_attr 'age', Number

class Photo extends Model
  @_attr 'caption', String
var // ...

Model = (function() {
  function Model() {}

  Model._attrs = null;

  Model._attr = function(name, type) {
    this._attrs || (this._attrs = {});
    this._attrs[name] = type;
    return this.prototype[name] = null;
  };

  return Model;

})();

User = (function(_super) {
  __extends(User, _super);

  function User() {
    _ref = User.__super__.constructor.apply(this, arguments);
    return _ref;
  }

  User._attr('name', String);

  User._attr('age', Number);

  return User;

})(Model);

Photo = // ...
# Executive Branch
class Person

  get = (props) =>
    @::__defineGetter__ name, func for name, func of props

  set = (props) =>
    @::__defineSetter__ name, func for name, func of props

  get age: -> (Date.now() - @birthdate) / MILLIS_PER_YEAR

  get birthdate: -> @_birthdate

  set birthdate: (val) ->
    throw new Error 'Birthdates are immutable.'

...
  ![Phew!](/images/coffeescript-talk/phew-businessman.jpg)
[![](/images/coffeescript-talk/coffeescript-hp.png)](http://coffeescript.org/)
[![](/images/neo4j-lessons-learned/fiftythree-hp.png)](http://www.fiftythree.com/)
# Thanks!   ### Twitter: [@aseemk](https://twitter.com/aseemk) ### GitHub: [@aseemk](https://github.com/aseemk) ### Email: [aseem.kishore@gmail.com](mailto:aseem.kishore@gmail.com)   Questions?