ReasonML
- facebook製プログラミング言語
- JavaScript、バイナリ両方にコンパイル可能
- OCamlとJavaScriptのエコシステムが利用できる
- 強力な型付けとイミュータブルが特徴
- 2018/02/21 時点で最新バージョンは
3.0.4
BuckleScript
- OCamlのJavaScriptバックエンド
- Bloomberg社製
- ReasonMLはBuckleScriptに乗っかっている
- 2018/02/21 時点で最新バージョンは
2.2.0
アーキテクチャ
https://github.com/facebook/reason/tree/master/src より引用
構文
- パターンマッチ
- ファンクター(Module Functions)
- パイプ演算子
module IntPairs = {
type t = (int, int);
let compare = ((x0, y0), (x1, y1)) => {
switch (Pervasives.compare(x0, x1)) {
| 0 => Pervasives.compare(y0, y1)
| c => c
}
};
};
module PairsSet = Set.Make(IntPairs);
let m = PairsSet.(empty |> add((2, 3)) |> add((5, 7)) |> add((11, 13)));
Js.log(PairsSet.min_elt(m)); /* => [2, 3] */
- これをコンパイルすると、
'use strict';
var $$Set = require("bs-platform/lib/js/set.js");
var Curry = require("bs-platform/lib/js/curry.js");
var Caml_obj = require("bs-platform/lib/js/caml_obj.js");
function compare(param, param$1) {
var c = Caml_obj.caml_compare(param[0], param$1[0]);
if (c !== 0) {
return c;
} else {
return Caml_obj.caml_compare(param[1], param$1[1]);
}
}
var IntPairs = /* module */[/* compare */compare];
var PairsSet = $$Set.Make(IntPairs);
var m = Curry._2(PairsSet[/* add */3], /* tuple */[
11,
13
], Curry._2(PairsSet[/* add */3], /* tuple */[
5,
7
], Curry._2(PairsSet[/* add */3], /* tuple */[
2,
3
], PairsSet[/* empty */0])));
console.log(Curry._1(PairsSet[/* min_elt */20], m));
exports.IntPairs = IntPairs;
exports.PairsSet = PairsSet;
exports.m = m;
レコード
- イミュータブルなオブジェクト
/* ここの型宣言がないと型推論に失敗してコンパイルエラー */
type r = {
a: int,
b: int
};
let r1 = {
a: 1,
b: 2
};
let r2 = {
...r1,
a: 3
};
Js.log(r1.a); /* => 1 */
Js.log(r2.a); /* => 3 */
- オブジェクトではなく、配列として出力される
- イミュータブルという前提があるため最適化がうまく効いていそう
var r2 = /* record */[
/* a */3,
/* b */2
];
console.log(1);
console.log(3);
var r1 = /* record */[
/* a */1,
/* b */2
];
exports.r1 = r1;
exports.r2 = r2;
演算子の独自定義
let (<$>) = (a, b) => a + b;
Js.log(1 <$> 2); /* => 3 */
ReasonReact
- ReactのReasonMLバインディング
- Reduxも一部含む
- Routerも一部含む
type action =
| ChangePage(ReasonReact.reactElement);
type state = {
page: ReasonReact.reactElement,
};
let component = ReasonReact.reducerComponent("App");
let make = (_children) => {
...component,
initialState: () => {
{page: <TopPage />}
},
reducer: (action, state) =>
switch (action) {
| ChangePage(page) => ReasonReact.Update({...state, page})
},
subscriptions: self => [
Sub(
() =>
ReasonReact.Router.watchUrl(url =>
switch(url.path) {
| [] => self.send(ChangePage(<TopPage />))
| ["hello", message] => self.send(ChangePage(<HelloPage message=message />))
| _ => self.send(ChangePage(<ErrorPage />))
}
),
ReasonReact.Router.unwatchUrl
)
],
render: self => self.state.page
};
テスト
- Jestが動くらしいが試せていない
コレクションライブラリ
- Immutable.re
- ReasonML純正ライブラリ
- 実験段階。Immutable.jsのバインディングを利用するよう公式が言っている
- githubのコミットも半年前ぐらいでストップ
- rationale
- Ramda.jsリスペクト
- 最近作られたばかり
- Haskellライクな独自演算子を定義
個人的に気になっているところ
- JSXの構文に少し癖がある
- テキストノードをそのままかけず、
stringToElement
という関数を呼ぶ必要がある -
<div>ReasonReact.stringToElement("Hello")</div>
とすると>
の後ろにスペースがなく構文エラー
- テキストノードをそのままかけず、
- やっぱりOCaml知らないとつらそう
あまり流行ってない-
ファイル === モジュール
という考え方- ディレクトリ構造は無視されてフラットに外部ファイルが呼べてしまう
- importみたいな構文なく、ファイル名をモジュール名として使ってアクセスできてしまう