こんにちは、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とともにあらんことを。