Reasonは関数型言語のOCamlの構文をJavaScript風に書けるようにしたプログラミング言語です。Reasonが面白いのは、JavaScriptにトランスパイルして、ブラウザ上で実行することもできますし、OCamlを介して実行ファイルに変換することもできる点にあります。

  • Reasonは、OCamlとJSの良い所をミックスした言語

    Reasonは、OCamlとJSの良い所をミックスした言語

Reasonの生い立ち

「Reason」は、Meta(旧Facebook)によって、2016年に公開されたプログラミング言語です。主開発者のジョーダン・ウォーク氏は、当時Metaの社員であり、有名なJavaScriptのフレームワークReactの初期設計者の一人でもあります。

ジョーダン・ウォーク氏は、ReactのUIをもっと安全に書きたいという個人的な問題意識から、Reasonを開発しました。その際、新しい言語を一から作るのではなく、実績ある関数型言語のOCamlのエコシステムをそのまま使って、構文だけをJavaScript風にしました。

現在、Reasonのバージョンは、3.17.2が公開されていますが、2017年10月にバージョン3が公開されて以降、大きな変更はないものの、OSS(オープンソース)のプロジェクトとして有志によって地道なメンテナンスが続いています。

なお、Reasonは現在大きな機能追加は行われておらず、JavaScript向けの後継言語として、ReScriptの開発が精力的に行われています。今回は、Reasonを扱いますが、ReScriptも面白いので興味があれば見てみると良いでしょう。

ReasonはOCamlのエコシステムを活用できる

興味深いことに、ReasonはOCamlの資産を全て利用できるように設計されています。と言うのも、OCamlは、構文解析のためにAST(Abstract Syntax Tree/抽象構文木)を作成しますが、ReasonはコードをOCamlのASTに変換します。

OCamlについては、本連載の27回目で解説していますが、1996年に開発され、確固とした人気を誇るプログラミング言語です。OCamlのために洗練されたコンパイラや便利なツールが整っています。そのため、OCamlのエコシステムを利用できるのは、それだけで大きなメリットとなります。

  • Reasonの構造について

    Reasonの構造について

例えば、以下の二つのコードは、二つの引数を掛け算するだけの関数を定義したものですが、見た目が異なるだけで、同じASTを生成します。

# Reason
let mul = (x, y) => x * y; /* 関数の定義 */
mul(3, 5) -> string_of_int -> print_endline /* 関数を利用して画面に結果を出力 */


# OCaml
let mul x y = x * y  /* 関数の定義 */
((mul 3 5) |> string_of_int) |> print_endline /*  関数を利用して画面に結果を出力 */

実際に、こちらにアクセスすると、ブラウザから、ReasonのコードをOCamlとJavaScriptに変換し、実行してくれます。

  • リアルタイムにOCaml/JSに変換して実行結果を表示してくれる

    リアルタイムにOCaml/JSに変換して実行結果を表示してくれる

改めて、Reasonのコードに注目してみると、JavaScriptで無名関数を定義する書式と同じ『(引数) => {関数内容}』を利用して関数を定義しています。関数型プログラミングは、ちょっと取っつきにくいところがありますが、JavaScriptを知っている人であれば、なんとなく、コードを読むことができるものとなっています。

また、Reasonのコードの2行目にある「->」はパイプ演算子と呼ばれており、左側に書いた式や値を右側の関数に適用するものとなっています。

Reasonをもう少し深掘りしてみよう

Reasonでは、変数に束縛した値を変更できないようにする「イミュータブル(immutable)」を強く推奨する方向となっています。

let x = 10;
x = 20; /* ここでエラーになる */

もちろん、変更を可能にする仕組みもありますが、その場合、値が変更されることを、きっちりと明示する必要があります。

let x = ref(10); /* 可変参照であることを明示 */
x := 20; /* エラーにならない */

また、OCaml由来の型推論の機能があるので便利です。いろいろな値をまとめて扱うのに便利なレコードを利用してみましょう。

/* person型のレコードを宣言 */
type person = {
  name: string,
  age: int,
};
/* person型のレコードを作成 (型推論が働くので明示的な宣言は不要) */
let mike = {
  name: "Mike",
  age: 30
};
/* person型のフィールドにアクセスする */
print_endline("Hello, " ++ mike.name) /* 「Hello, Mike」と表示される */

このように、変数mikeがperson型であることが自動的に推論されて、安全にそのフィールドにアクセスできます。

ただし、レコードも何も宣言しない場合はイミュータブル(immutable)となります。値を変更したい時には、型を定義する際、mutableという宣言を行う必要があります。

/* レコード型personを宣言 */
type person = {
  name: string,
  mutable age: int /* 変更可能なことを宣言 --- (*1) */
};
/* person型のレコードを作成(型推論が働くので明示的な宣言は不要) */
let mike = {name: "Mike", age: 30};
mike.age = 31; /* ageは変更可能 */

上記のプログラムの(*1)の部分がポイントで、mutableを付けているため、レコード型personのフィールドageが変更可能となります。

こうした仕組みがあるので、不用意に変更してはならないフィールドを変更してしまうというバグを防ぐことができます。

RustやSwift、Kotlinなど現代的なプログラミング言語には、こうした仕組みが備わっており、状態の変更は危険なので明示的に宣言する必要があるという流れになっています。

FizzBuzz問題を解いてみよう

それでは、最後にReasonでFizzBuzz問題を解いてみましょう。FizzBuzz問題とは、1から数え上げながら、3の倍数なら「Fizz」、5の倍数なら「Buzz」、両方の倍数(15の倍数)なら「FizzBuzz」、それ以外は数字を表示するという有名な課題です。

/* FizzBuzzを返す関数を宣言 ---(*1) */
let isFizz = (n: int) => (n mod 3 == 0)
let isBuzz = (n: int) => (n mod 5 == 0)
let fizzBuzz = (n: int) =>
    /* FizzとBuzzの条件で分岐 --- (*2) */
    switch ((isFizz(n), isBuzz(n))) {
    | (true, true) => "FizzBuzz"
    | (true, false) => "Fizz"
    | (false, true) => "Buzz"
    | (false, false) => string_of_int(n)
    };
/* FizzBuzzを1から100まで呼び出す --- (*3) */
List.init(100, i => i + 1) /* 1から100の連番を持つリストを作る */
|> List.map(fizzBuzz) /* リストの各要素にfizzBuzzを適用 */
|> List.iter(print_endline); /* リストを順番に出力 */

先ほどのブラウザツールでReasonを実行してみると、次のように表示されます。右下のCONSOLEにFizzBuzzの結果が表示されます。

  • FizzBuzz問題をReasonで解いたところ

    FizzBuzz問題をReasonで解いたところ

プログラムを確認しましょう。(*1)では、最初にisFizz、isBuzzの関数を定義し、FizzかBuzzかを判定します。そして、関数fizzBuzzは与えられたnに対するFizzBuzzの結果を返すものとなっています。(*2)の部分で、switch文を使うことで、isFizzとisBuzzの状態に応じた値を返すことができるようにしています。(*3)では、1から100までのListを作成し、その要素全てに対して、関数fizzBuzzを適用した上で、リストを出力します。

Reasonのまとめ

以上、今回は関数型言語 Reason を紹介しました。関数型言語というと、どうしても難しそうな印象を持たれがちですが、Reason は文法を JavaScript に寄せることで、「関数型言語でもなんとか読めそうだ」という感覚をうまく演出してくれます。

また、OCaml の成熟したエコシステムをそのまま活用できる点も、大きな魅力です。学習コストは多少かかりますが、一度理解すれば「一粒で二度おいしい」言語だと感じました。一方で、Reason 初心者の視点で見ると、Reason 的には正しく書けていそうなコードでも、OCaml 側のエラーに悩まされる場面があります。結局のところ、Reason を使いこなすには OCaml の知識もある程度必要になり、その点にジレンマを感じることもありました。

それでも、JavaScript に近い見た目で関数型プログラミングを体験できる点は大きな価値があると感じました。関数型言語への心理的なハードルをぐっと下げてくれる言語です。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「実践力をアップする Pythonによるアルゴリズムの教科書(マイナビ出版)」「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。