(ε・◇・)з CoffeeScript と mofmof.js

(ε・◇・)з CoffeeScriptRubyPython っぽく書けて JavaScriptコンパイルできるプリティな言語だとか!
(ε・◇・)з 噂では CoffeeScript で書くとコードが短くなるとか!!

物は試しに http://jashkenas.github.com/coffee-script/ にある幾つかのコード片と、mofmof.js 混じりで書いた場合のコードを比べてみました。

(ε・◇・)з mofmof.js はここだよ → http://code.google.com/p/mofmof-js/w/list

Syntax
// coffee

    # Assignment:
    number   = 42
    opposite = true

    # Conditions:
    number = -42 if opposite

    # Functions:
    square = (x) -> x * x

    # Arrays:
    list = [1, 2, 3, 4, 5]

    # Objects:
    math =
      root:   Math.sqrt
      square: square
      cube:   (x) -> x * square x

    # Splats:
    race = (winner, runners...) ->
      print winner, runners

    # Existence:
    alert "I knew it!" if elvis?

    # Array comprehensions:
    cubes = (math.cube num for num in list)

// coffee -> js ----------------------------

    var cubes, list, math, num, number, opposite, race, square;
    var __slice = Array.prototype.slice;
    number = 42;
    opposite = true;
    if (opposite) number = -42;
    square = function(x) {
      return x * x;
    };
    list = [1, 2, 3, 4, 5];
    math = {
      root: Math.sqrt,
      square: square,
      cube: function(x) {
        return x * square(x);
      }
    };
    race = function() {
      var runners, winner;
      winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      return print(winner, runners);
    };
    if (typeof elvis !== "undefined" && elvis !== null) alert("I knew it!");
    cubes = (function() {
      var _i, _len, _results;
      _results = [];
      for (_i = 0, _len = list.length; _i < _len; _i++) {
        num = list[_i];
        _results.push(math.cube(num));
      }
      return _results;
    })();

// mofmof ----------------------------

    // Assignment:
    var number = 42, opposite = true;

    // Conditions:
    opposite && (number = -42);

    // Functions:
    function square(x) { return x * x; }

    // Arrays:
    list = [1, 2, 3, 4, 5];

    // Objects:
    math = {
        root:   Math.sqrt,
        square: square,
        cube:   function(x) { return x * square(x); }
    };

    // Splats:
    function cube(winner, runners, /*... */) {
        return print.apply(null, arguments);
    }

    // Existence:
    elvis != null && alert("I knew it!");

    // Array comprehensions:
    cubes = list.filter(math.cube);

(ε・◇・)з 基本 Syntax だと、あまり変わった感じしませんね〜

Functions
// coffee

    square = (x) -> x * x
    cube   = (x) -> square(x) * x

    fill = (container, liquid = "coffee") ->
      "Filling the #{container} with #{liquid}..."

// coffee -> js ----------------------------

    var cube, square;
    square = function(x) {
      return x * x;
    };
    cube = function(x) {
      return square(x) * x;
    };
    var fill;
    fill = function(container, liquid) {
      if (liquid == null) liquid = "coffee";
      return "Filling the " + container + " with " + liquid + "...";
    };

// mofmof.js ----------------------------

    function square(x) { return x * x; };
    function cube(x)   { return square(x) * x; }

    function fill(container, liquid /* = coffee */) {
        return "Filling the @@ with @@...".f(container, liquid || "coffee");
    }

(ε・◇・)з Coffee は function が -> になってる分だけタイプ数少なめですね

Loops and Comprehensions
// coffee
    # Eat lunch.
    eat food for food in ['toast', 'cheese', 'wine']

// coffee -> js ----------------------------

    var food, _i, _len, _ref;
    _ref = ['toast', 'cheese', 'wine'];
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      food = _ref[_i];
      eat(food);
    }

// mofmof ----------------------------

    ['toast', 'cheese', 'wine'].forEach(eat);

            or

    var iter = Hash(['toast', 'cheese', 'wine']), food;
    while (food = iter.next()) {
        eat(food);
    }

(ε・◇・)з mofmof.js はforEachやイテレータ使ってます。実行速度が重要でなければ、このへんは好みかな〜

// coffee

    countdown = (num for num in [10..1])

// coffee -> js ----------------------------

    var countdown, num;
    countdown = (function() {
      var _results;
      _results = [];
      for (num = 10; num >= 1; num--) {
        _results.push(num);
      }
      return _results;
    })();

// mofmof ----------------------------

    countdown = 10..$(1);

(ε・◇・)з mofmof.js は Number.prototype.$ を拡張して 10..$(1) で 10 から1までの配列を生成していますよ〜

// coffee

    yearsOld = max: 10, ida: 9, tim: 11

    ages = for child, age of yearsOld
      "#{child} is #{age}"

// coffee -> js ----------------------------

    var age, ages, child, yearsOld;
    yearsOld = {
      max: 10,
      ida: 9,
      tim: 11
    };
    ages = (function() {
      var _results;
      _results = [];
      for (child in yearsOld) {
        age = yearsOld[child];
        _results.push("" + child + " is " + age);
      }
      return _results;
    })();

// mofmof ----------------------------
    var yearsOld = { max: 10, ida: 9, tim: 11 };
    Hash.map(yearsOld, function(age, child) {
        return "@@ is @@".f(child, age);
    });

(ε・◇・)з Coffee側のコードはシンプルですね〜

Array Slicing and Splicing with Ranges
// coffee
    numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    copy    = numbers[0...numbers.length]

    middle  = copy[3..6]

// coffee -> js ----------------------------
    var copy, middle, numbers;
    numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    copy = numbers.slice(0, numbers.length);
    middle = copy.slice(3, 7);

// mofmof ----------------------------
    var numbers = 0..$(9);

    var copy    = numbers.clip(0, numbers.length);

    var middle  = copy.clip(3, 6);

(ε・◇・)з もふもふ!

// coffee
    numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    numbers[3..6] = [-3, -4, -5, -6]

// coffee -> js ----------------------------
    var numbers, _ref;
    numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    [].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;

// mofmof ----------------------------
    numbers = 0..$(9);
    numbers.swap(3, (-3).$(-6));

(ε・◇・)з mofmof 側は、Array#swap で配列の一部をごっそり入れ替えてます〜

Everything is an Expression (at least, as much as possible)
// coffee
    globals = (name for name of window)[0...10]

// coffee -> js ----------------------------
    var globals, name;
    globals = ((function() {
      var _results;
      _results = [];
      for (name in window) {
        _results.push(name);
      }
      return _results;
    })()).slice(0, 10); // 全部とってきて10個だけ返す

// mofmof ----------------------------
    globals = Hash.keys(window, 10); // 10個とってきて返す

(ε・◇・)з mofmof の Hash.keys は、第二引数で列挙する要素の最大数を指定できるので、10個だけ列挙して返します〜

Classes, Inheritance, and Super
// coffee

    class Animal
      constructor: (@name) ->

      move: (meters) ->
        alert @name + " moved #{meters}m."

    class Snake extends Animal
      move: ->
        alert "Slithering..."
        super 5

    class Horse extends Animal
      move: ->
        alert "Galloping..."
        super 45

    sam = new Snake "Sammy the Python"
    tom = new Horse "Tommy the Palomino"

    sam.move()
    tom.move()


// coffee -> js ----------------------------
    var Animal, Horse, Snake, sam, tom;
    var __hasProp = Object.prototype.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;
    };
    Animal = (function() {
      function Animal(name) {
        this.name = name;
      }
      Animal.prototype.move = function(meters) {
        return alert(this.name + (" moved " + meters + "m."));
      };
      return Animal;
    })();
    Snake = (function() {
      __extends(Snake, Animal);
      function Snake() {
        Snake.__super__.constructor.apply(this, arguments);
      }
      Snake.prototype.move = function() {
        alert("Slithering...");
        return Snake.__super__.move.call(this, 5);
      };
      return Snake;
    })();
    Horse = (function() {
      __extends(Horse, Animal);
      function Horse() {
        Horse.__super__.constructor.apply(this, arguments);
      }
      Horse.prototype.move = function() {
        alert("Galloping...");
        return Horse.__super__.move.call(this, 45);
      };
      return Horse;
    })();
    sam = new Snake("Sammy the Python");
    tom = new Horse("Tommy the Palomino");
    sam.move();
    tom.move();

// mofmof ----------------------------
    mm.Class("Animal", {
        init: function(name) {
            this.name = name;
        },
        move: function(meters) {
            alert("@@ moved @@m.".f(this.name, meters));
        }
    });
    mm.Class("Snake:Animal", {
        move: function() {
            alert("Slithering...");
            this.superCall("move", 5);
        }
    });
    mm.Class("Horse:Animal", {
        move: function() {
            alert("Galloping...");
            this.superCall("move", 45);
        }
    });
    var sam = mm("Snake", "Sammy the Python");
    var tom = mm("Horse", "Tommy the Palomino");

    sam.move();
    tom.move();

(ε・◇・)з mofmof にもクラスあるんだよ〜
(ε・◇・)з 最大継承数は1段に限定してある(昔は3段までサポートしてた)けど、1段継承できればニーズの9割カバーできるからOKなんだよ〜 (それ以上継承したい人は変態的なコードを書きたい人だけだと思うよ〜

Destructuring Assignment
// coffee
    theBait   = 1000
    theSwitch = 0

    [theBait, theSwitch] = [theSwitch, theBait]

// coffee -> js ----------------------------
    var theBait, theSwitch, _ref;
    theBait = 1000;
    theSwitch = 0;
    _ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];

// mofmof ----------------------------
    var theBait = 1000, theSwitch = 0;

    [theBait, theSwitch].swap(0, [theSwitch, theBait]);

(ε・◇・)з ここでも Array#swap が出てきてます〜

Chained Comparisons
// coffee
    cholesterol = 127

    healthy = 200 > cholesterol > 60

// coffee -> js ----------------------------
    var cholesterol, healthy;
    cholesterol = 127;
    healthy = (200 > cholesterol && cholesterol > 60);

// mofmof ----------------------------
    var cholesterol = 127

    healthy = cholesterol.inRange(200, 60);

(ε・◇・)з Coffee のほうが分かりやすいね〜

おしまい

(ε・◇・)з mofmof.js も結構イケてるんじゃないでしょうか!