BuckleScriptはJavaScriptコードを生成するOCamlコンパイラ。OCamlで型安全なプログラミングを行い、そこから可読性の高いJavaScriptコードを生成できる。
BuckleScriptは、JavaScriptコードを生成するOCamlコンパイラだ。ブルームバーグが開発し、オープンソースソフトウェアとして公開している(BuckleScriptはOCamlをJavaScriptに変換するコンパイラプロジェクトである「js_of_ocaml」から派生したプロジェクト)。
JavaScriptの活用範囲は既にWebブラウザを飛び出して、サーバサイドやモバイルアプリ、デスクトップアプリにまで広がっている。そして、多くのPCやデバイスには何らかの形でJavaScriptエンジンが搭載されている。さらに、JavaScript実行環境間での互換性も高まりつつある。すなわち、JavaScriptは多くの環境で同様に動作するプラットフォームとなっている。OCamlコードをJavaScriptコードにコンパイル(トランスパイル)できれば、JavaScriptがサポートされている環境(=ほぼ全ての環境)でOCamlプログラムを実行できるようになるのは魅力的なところだ。
その一方で、JavaScriptは柔軟な記述が可能な動的型付け言語であり、大規模開発には向いていないといわれている。この点を埋めるべくTypeScriptでは、静的な型注釈を付加し、JavaScriptコードへのトランスパイル時に静的な型チェックを行うことで、JavaScriptベースの言語であっても大規模開発を行えるようにしている。
BuckleScriptはこうした流れの中で生まれたものだといえる。ただし、そこで使われる言語がOCamlというのが大きな違いである。OCamlは静的型付けの関数型言語であり、強力な型推論機能を持っているのが大きな特徴だ。コンパイル時には静的解析が行われた後は、実行中に何らかのタイプエラーが発生することはない。さらに簡潔な記述が可能であることも相まって、多くの企業で実際に利用されている実績のある言語だ(BuckleScriptを開発しているブルームバーグ自体がOCamlのユーザーとなっている)。
「BuckleScript User Manual」ページでは、BuckleScriptの特徴として以下が挙げられている。
これらの多くは、OCamlが持つ型システムとコンパイラチェーンによる恩恵だ。例えば、コンパイル時の静的解析により、関数やモジュールレベルでデッドコード(呼び出されることがないコード)が検出/削除される。これはJavaScriptコードの実行時にVMが無駄なコードを評価することがなくなると同時に、出力されるコードがコンパクトになることを意味している。
BuckleScriptを試してみるにはWebブラウザを使うのが一番簡単だ。ここでは「Playground」ページで実際にこれを試してみよう。
Playgroundページを開くと以下のコードが左側のペーンに、そのコンパイル結果が右側のペーンに表示されている。
print_endline "Hello BuckleScript!"
元のコードもコンパイル結果もコンソールに文字列を出力するだけなので、まずは関数を定義して、それを呼び出すようにしてみよう。
let hello x = print_endline ("Hello " ^ x)
let () = hello "World"
2行目の「let () = ……」はこのスクリプトのエントリポイントとなり、"World"を引数としてhello関数を呼び出している。コンパイル結果は次のようになる。
'use strict';
function hello(x) {
console.log("Hello " + x);
return /* () */0;
}
console.log("Hello World");
exports.hello = hello;
hello関数が等価なJavaScriptコードになっているのが確認できる。が、ここで注意したいのはconsole.logメソッド呼び出しが2つある点だ。そして、2つ目のconsole.logメソッド呼び出しでは引数が「"Hello World"」となっている。つまり、このプログラムを実行しても、hello関数は呼び出されない。これはOCamlコードのコンパイル時に行われた最適化の結果だろう。Playgroundページの右上には[Remove unused code]チェックボックスがあるので、これをオンにすると、JavaScriptコードが表示されているペーンがさらにスッキリしたものになるので、確かに使われていないことが分かる。
次に再帰関数を2つ定義してみる。1つは通常の再帰関数で、もう1つは末尾再帰を行う。
let rec fact n =
if n = 0 then 1
else n * fact(n - 1)
let rec fact2(n, count) =
if n = 0 then count
else fact2 (n-1, n * count)
let () =
print_endline(string_of_int(fact(4)));
print_endline(string_of_int(fact2(4, 1)))
コンパイル後のコードは次のようになる。
'use strict';
var Pervasives = require("stdlib/pervasives");
var Caml_int32 = require("stdlib/caml_int32");
function fact(n) {
if (n) {
return Caml_int32.imul(n, fact(n - 1 | 0));
}
else {
return 1;
}
}
function fact2(_param) {
while(true) {
var param = _param;
var count = param[1];
var n = param[0];
if (n) {
_param = /* tuple */[
n - 1 | 0,
Caml_int32.imul(n, count)
];
continue ;
}
else {
return count;
}
};
}
console.log(Pervasives.string_of_int(fact(4)));
console.log(Pervasives.string_of_int(fact2(/* tuple */[
4,
1
])));
exports.fact = fact;
exports.fact2 = fact2;
通常の再帰関数は再帰を行うJavaScriptコードに、末尾再帰を行うOCamlコードは展開されてwhileループを使って計算を行うようにコンパイルされたことが分かる(コードの詳細な解説は省略する)。
ここで注意したいのは、最初にある2つのrequire関数だ。最初にインポートしている「Pervasives」モジュールはOCamlの組み込み型に対する基本操作を定義しているモジュールだ(OCamlのPervasivesモジュールがJavaScriptコードにマッピングされている)。ここでは整数を文字列変換するstring_of_int関数を使用している。また、Caml_int32モジュールには整数の乗除演算など幾つかの演算が定義されており、ここでは32bit整数の乗算を行うCaml_int32.imul関数を利用している。
2つのコードを比べると、OCamlのコードが極めて簡潔であることと、出力されたJavaScriptコードの可読性の高さが感じられる。また、生成されたコードはJavaScriptモジュールとなっているので、JavaScriptコードから利用可能だ(逆に、OCmalコード内でFFI=Foreign Function Interfaceを使用して、JavaScriptオブジェクトを作成/操作することも可能だ。詳細については「BuckleScript User Manual」を参照されたい)。
なお、BuckleScriptはnpm経由でローカル環境にインストールすることも可能だ(インストール時にBuckleScriptのビルドが行われるため、Cコンパイラツールチェーンが必要)。
BuckleScriptは、JavaScriptコードを生成するOCamlコンパイラだ。OCamlが持つ強力な型システムを活用することで、冗長な型注釈を記述することなく、型安全なプログラミングを行い、そこから可読性の高いJavaScriptコードを生成できる。また、本稿では取り上げなかったが、OCamlコードとJavaScriptコードとの相互運用性などもサポートされており、OCamlコードからJavaScriptコードを呼び出したり、その逆を行ったりすることも可能だ。
Copyright© Digital Advantage Corp. All Rights Reserved.