wataメモ

日々のメモをつらつらと書くだけ

Spineを使って・・・と思ったらCoffeeScriptを使っていた

 Backbone.jsを試したので次はSpineを試そうと考えて、ダウンロードしに行こうとしたらどうやらnode.jsがあった方がいいということが分かりインストールした。そしてSpineはCoffeeScriptで記載されている事を知る。別にSpineを使うだけならCoffeeScriptはいらないのだが、前から気にはなっていたので今回使ってみる事にした。

 CoffeeScriptはJavaScriptRubyPythonの様に記述出来るように構文拡張した感じの言語だ。なのでCoffeeScriptで記述→JavaScriptにコンパイル→アプリで使用の流れになる。基本こういう「変換係」の言語は痒いところに手が届かない、エラーが分かりづらい系が多いと考えているので、今回も「まあ、触ってみるか」程度に考えていた。しかし、触ってみるとそんな印象は受けず、記述が楽に出来るような物だった。

 さて、使い方だがコンパイル言語なのでコマンドラインでコンパイルする必要がある。Node.jsのモジュールとして提供されているのでまずはインストールする。

npm install -g coffee-script

 お試しレベルならJavaScript版の動的コンパイラも提供されているらしい。

<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js" type="text/javascript" charset="utf-8"></script>
<script type="text/coffeescript">
  // なにかしらのCoffeeScript
</script>

 コンパイルするにはcoffeeコマンドで行う。毎回行なっても良いが、ファイルの変更を検知して自動コンパイルするようにも出来る。

coffee -c test.coffee -o out/ # 都度コンパイル
coffee -w -j out/project.js -c -o out/ src/*.coffee # 更新を検知して自動コンパイル(複数のファイルをproject.jsに纏めて出力もする)

  準備が整ったところで簡単にCoffeeScriptの構文のまとめ。

  1. セミコロンの削除。(CoffeeScriptがJavaScriptにする時に必要な箇所に埋め込む)
  2. コメントは「#」複数行コメントは「###」。
  3. タブが「{}」の代わりになる。(Pythonライクな考え方。整っていないとコンパイルも出来ないので、コードの書き方が必ず統一される。)
  4. varの削除。(CoffeeScriptが必ず固定でvar宣言を頭で行う。global変数も一応作ることも出来る。)
  5. 関数宣言は「->」(細い矢印)で書く。
  6. 関数呼び出しは引数が1つでもあれば「()」が省略可能。(alert a はalert(a)と同義)
  7. ハッシュは配列の定義の簡略化。
  8. 制御構文の便利化。(ifやfor等)
  9. Stringの文字補完。("hello #{name}!"と書くと"hello " + name + "!"に変換される)
  10. 配列のslice。(Rubyライクなレンジ指定のsliceが可能「firstTwo = ["one", "two", "three"][0..1]」)
  11. thisの代わりに「@」。(this.variableは@variableと書く)
  12. prototypeの代わりに「::」。
  13. nullチェックに「?」を使う。(if brian?はbrianがnullかundefinedの時にtrueになる)
  14. classの定義を簡略化。(「class Animal」でAnimalクラスが出来る)
  15. 継承とsuper。(class Horse extends Animalで継承、super()で親の関数実行)
  16. 「`」で囲えばnativeなJavaScriptが記述可能。

 上記の中で補足が必要そうなところをいくつか紹介。まずは関数宣言について。具体的には以下のように記述する。

# 二乗する関数
square = (x) -> x * x
// JavaScriptのソースは以下になる
square = function(x) {
  return x * x;
};
# 呼び出し方
square 5

  最後に評価された値が戻り値となるので「x * x」が戻り値となっている。複数行になる関数を書く場合は以下のようにインデントを入れて書く。渡された数を1桁ずつ足した和を返す関数を書いてみる。

add_num = (x) ->
 sum = 0
 sum += parseInt n, 10 for n in x.toString().split ''
 sum
 # 呼び出し
 alert add_num 1234
// JavaScript的には以下になる
var add_num;
add_num = function(x) {
  var n, sum, _i, _len, _ref;
  sum = 0;
  _ref = x.toString().split('');
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    n = _ref[_i];
    sum += parseInt(n, 10);
  }
  return sum;
};

alert(add_num(1234)); // 結果は10となる

 連想配列、配列の定義方法も若干短く書けるようになっている。

# 配列
song = ["do", "re", "mi", "fa", "so"]
// JavaScriptのソース
song = ["do", "re", "mi", "fa", "so"];
# 連想配列
kids =
 brother:
  name: "Max"
  age:  11
 sister:
  name: "Ida"
  age:  9
// JavaScriptのソース
kids = {
  brother: {
    name: "Max",
    age: 11
  },
  sister: {
    name: "Ida",
    age: 9
  }
};

  配列の場合はセミコロンしか短くなっていないが改行を入れればカンマは省略出来る。これは微妙だが改行の前か後にカンマをつける戦争を無くすことは出来るのだろう。

 次は制御構文について。まずはifから。

# 1行if
mood = greatlyImproved if singin
// JavaScriptのソース
if (singin) {
  mood = greatlyImproved;
}
# 3項演算子の代わり
date = if friday then sue else jill
// JavaScriptのソース
date = friday ? sue : jill;
# 通常のif else
if happy and knowsIt
 clapsHands()
 chaChaCha()
else
 showIt()
// JavaScriptのソース
if (happy && knowsIt) {
  clapsHands();
  chaChaCha();
} else {
  showIt();
}

  1行ifは慣れないと違和感を感じるかもしれないが、慣れている人には馴染みやすい。「?」が特殊な演算子になっているので、通常の3項演算子が書けない。「then」「else」を使うことで1行でかけるが通常のifの書き方で、代入形式で書けば3項演算子になる。

 次はfor。

# 1行for
eat food for food in ['toast', 'cheese', 'wine']
# 1行forでインデックスが欲しい場合
menu i + 1, dish for dish, i in courses
# 1行forで条件を指定したい時
eat food for food in foods when food isnt 'chocolate'
// JavaScriptのソース
var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;

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

courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];

for (i = _j = 0, _len1 = courses.length; _j < _len1; i = ++_j) {
  dish = courses[i];
  menu(i + 1, dish);
}

foods = ['broccoli', 'spinach', 'chocolate'];

for (_k = 0, _len2 = foods.length; _k < _len2; _k++) {
  food = foods[_k];
  if (food !== 'chocolate') {
    eat(food);
  }
}

  1行forは個人的には好きなので書けるようになったのは嬉しい。しかもインデックスも取れるし、1行forと1行ifの組み合わせまで出来る。これは個人的には使って行きたいが、人によって好みが別れるだろう。

 最後はsliceとsplice。

# slice
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
start   = numbers[0..2]
middle  = numbers[3...6]
end     = numbers[6..]
copy    = numbers[..]
// JavaScriptのソース
var copy, end, middle, numbers, start;
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
start = numbers.slice(0, 3);
middle = numbers.slice(3, 6);
end = numbers.slice(6);
copy = numbers.slice(0);
# splice
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[3..6] = [-3, -4, -5, -6]
// JavaScriptのソース
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;

  spliceはそこまで強力ではなさそう(連想配列には使えない?)だが、今後の拡張に期待したいところだ。

 最後に感想としては、RubyPythonに慣れていないと学習コストが有るため、大人数での開発は立ち上がりにコストが掛かりそう。わかっている人達や慣れている人達で開発すれば記述量が減るのは魅力的かもしれない。なので個人アプリには是非導入して行きたいと思う。