こんにちは、SI部のfkmtです。
みなさん「JavaScirpt」はお好きでしょうか?
JavaScriptはブラウザで動く言語として、他に選択肢のない唯一の存在として君臨してきました。
好き嫌いに関わらず、Web開発者たちはJavaScriptを使わざるを得なかったわけです。
しかしそれも「CoffeeScript」に端を発した「AltJS」の登場で状況は大きく変わりました。
今やWeb開発者たちは使う言語を選択する自由を手に入れたのです!
・・・と仰々しい前置きで
今回はAltJSの1つ「LiveScript」をご紹介しようと思います。
なお、今回の投稿は以下のような方を読者として想定しております。
- JavaScriptも悪くないけど、他に何かないの? というルーク
- CoffeeScriptいいね〜、でもちょっと飽きてきたな というルーク
- AltJS多すぎて調べきれないよ、でも何か面白そうだな〜 というルーク
また、掲載コードは以下の環境で動作検証を行っています。
- NodeJS v4.1.2
- LiveScript v1.4.0
NodeJSとLiveScriptのセットアップは割愛させていただきます
LiveScriptとは
JavaScirptは昔々「LiveScript」と呼ばれていました。
紛らわしいですが、今回ご紹介するのはAltJSの「LiveScipt」です。
コンパイルするとJavaScriptになる、トランスコンパイラと呼ばれる類のモノです。
LiveScriptはCoffeeScriptの発展版なので、
CoffeeScriptの文法をご存知であればスグに乗り換えられます。
そこで今回は、CoffeeScriptと異なる点を中心にご紹介いたします。
リスト内包表記
これはCoffeeScriptにもあるのですが、非常に便利な機能ですのでぜひ。
JavaScript
やや作為的な例となりますが
0〜9の配列から、偶数のものを抽出し、1加算します。
var result = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].filter(function(it) { return it % 2 == 0; }).map(function(it) { return it + 1; });
LiveScript
LiveScriptで書くと、こうなります。
result = [ it + 1 for it in [0 to 9] when it % 2 is 0 ]
う〜ん、美しい・・・。
JavaScriptコードと比較してロジックの「ノイズ」が少なくなっているのをおわかりいただけますでしょうか。
[ {任意の式} for {変数名} in {配列} when {条件式} ]
という形式で、配列から抽出し、変換し、新たな配列を作ります。
Range
[0 to 9]
は [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
に変換されます。
RubyのRange的なものです。
ちなみにリスト内包表記はCoffeeScriptやLiveScriptだけではなく
PythonやHaskell、Erlangなどにもある普遍的な書き方です。
パイプ
シェルスクリプトなどで使う「|(パイプ)」的なことができます。
JavaScript
先ほどの例を「underscore.js」(http://underscorejs.org/)を使ってやってみます。
var _ = require('./underscore-min.js'); var result = _.map( _.filter( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], function(it) { return it % 2 == 0; } ), function(it) { return it + 1; } );
結果は同じですが、もう1例。
var _ = require('./underscore-min.js'); var result = _.chain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).filter(function(it) { return it % 2 == 0; }).map(function(it) { return it + 1; }).value();
LiveScript
LiveScriptで書くと、こうなります。
_ = require './underscore-min.js' result = [0 to 9] |> _.filter _, (-> it % 2 is 0) |> _.map _, (+ 1)
「|>」がパイプです。
パイプ左辺の処理の結果を、右辺に引き渡します。
右辺では左辺の結果を「_(アンダースコア)」で参照します。
パイプ以外のコールバック関数部分
(-> it % 2 is 0)
は function(it) {return it % 2 === 0}
に変換され、
(+ 1)
は function(it) {return it + 1}
に変換されます。
引数が1つの場合はデフォルトで「it」という変数名で参照します。
デストラクチャリング(Destructuring)
パターンマッチと呼ばれることもあります。
LiveScript
今度はLiveScriptから。
[first, second, ...rest] = [0 to 9] # first => 0 # second => 1 # rest => [ 2, 3, 4, 5, 6, 7, 8, 9 ]
JavaScript
JavaScriptでは。
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; var first = array[0]; var second = array[1]; var rest = array.slice(2);
地味ですが、便利です。
パターンマッチもデストラクチャリングも、同様の機能を表していますが
「パターンマッチ」という場合、
「=」を挟んで「左辺のオブジェクトを右辺の形式にマッチさせる」という含意があり「デストラクチャリング」という場合は、
「あるオブジェクトを任意の変数に分割して代入する」という意味を持たせていると思われます。
今回は分割代入にフォーカスして「デストラクチャリング」という言葉を選びました。
Backcalls
コールバックのネストをフラットに書き下すことができます。
例えば、以下3つのAPIがあるとします。
- api-A
- 「api-B」への入力を返す
- api-B
- 「api-A」の結果をパラメータにとり「api-C」への入力を返す
- api-C
- 「api-B」の結果をパラメータにとり何らかのメッセージを返す
api-A -> api-B -> api-C -> message
というイメージです。
JavaScript
JavaScriptで書くと。。。
$.get('api-A', function(responseA){ $.get('api-B', responseA, function(responseB){ $.get('api-C', responseB, function(lastResponse){ alert(lastResponse.message); }); }); });
LiveScript
これをLiveScriptでは。
do responseA <-! $.get 'api-A' responseB <-! $.get 'api-B', responseA lastResponse <-! $.get 'api-C', responseB alert lastResponse.message
なんということでしょう!
見苦しい関数のネスト、深淵なインデントがスッキリとフラットになり
ロジックの流れが素直に書き下せるように生まれ変わりました。
さらに省略して
以下のように書くこともできます。
do <-! $.get 'api-A' <-! $.get 'api-B', it <-! $.get 'api-C', it alert it.message
おわりに
さて今回は数ある「AltJS」の中から「LiveScript」をご紹介しました。
記述量を減らすための仕掛けがいろいろとあり、ハマると面白いようにコードを短縮できます。
しかしながら、書き方がいろいろと「ありすぎる」ために
システム全体で記述レベルを合わせることが難しく、大規模開発での採用には少々躊躇ってしまう面もあります。
幸い、AltJSは把握できないほどたくさんの種類がありますので、
プロジェクトの環境/条件/特性に応じて、お好きなものを選択できます。
AltJSたち: https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-js
ただ、どのAltJSを使うにせよ、結局はJavaScriptに変換されますので
JavaScriptはやはり抑えておく必要がありますね。
それでは、AltJSとともにあらんことを。