Spineを使って・・・と思ったらCoffeeScriptを使っていた
Backbone.jsを試したので次はSpineを試そうと考えて、ダウンロードしに行こうとしたらどうやらnode.jsがあった方がいいということが分かりインストールした。そしてSpineはCoffeeScriptで記載されている事を知る。別にSpineを使うだけならCoffeeScriptはいらないのだが、前から気にはなっていたので今回使ってみる事にした。
CoffeeScriptはJavaScriptをRubyやPythonの様に記述出来るように構文拡張した感じの言語だ。なので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の構文のまとめ。
- セミコロンの削除。(CoffeeScriptがJavaScriptにする時に必要な箇所に埋め込む)
- コメントは「#」複数行コメントは「###」。
- タブが「{}」の代わりになる。(Pythonライクな考え方。整っていないとコンパイルも出来ないので、コードの書き方が必ず統一される。)
- varの削除。(CoffeeScriptが必ず固定でvar宣言を頭で行う。global変数も一応作ることも出来る。)
- 関数宣言は「->」(細い矢印)で書く。
- 関数呼び出しは引数が1つでもあれば「()」が省略可能。(alert a はalert(a)と同義)
- ハッシュは配列の定義の簡略化。
- 制御構文の便利化。(ifやfor等)
- Stringの文字補完。("hello #{name}!"と書くと"hello " + name + "!"に変換される)
- 配列のslice。(Rubyライクなレンジ指定のsliceが可能「firstTwo = ["one", "two", "three"][0..1]」)
- thisの代わりに「@」。(this.variableは@variableと書く)
- prototypeの代わりに「::」。
- nullチェックに「?」を使う。(if brian?はbrianがnullかundefinedの時にtrueになる)
- classの定義を簡略化。(「class Animal」でAnimalクラスが出来る)
- 継承とsuper。(class Horse extends Animalで継承、super()で親の関数実行)
- 「`」で囲えば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はそこまで強力ではなさそう(連想配列には使えない?)だが、今後の拡張に期待したいところだ。
最後に感想としては、RubyやPythonに慣れていないと学習コストが有るため、大人数での開発は立ち上がりにコストが掛かりそう。わかっている人達や慣れている人達で開発すれば記述量が減るのは魅力的かもしれない。なので個人アプリには是非導入して行きたいと思う。