どこか遠くへ行きたい
今日は少し節約です。『知らない言語』じゃないので、看板に偽りあります。
既に 1 年前に調べ尽くしているので、今回は前回からの変更点のみをまとめました。
最近、ブラウザ拡張をひとつ ReasonML で書いているので、改めて変更点まとめておきたかったのもあります。
前回の記事 : Reason 使った感想 on Windows
ReasonML
公式サイト : https://reasonml.github.io/
公式ドキュメント : https://reasonml.github.io/docs/en/what-and-why
API ドキュメント : https://reasonml.github.io/api/index
リポジトリ : https://github.com/facebook/reason
Reason lets you write simple, fast and quality type safe code while leveraging both the JavaScript & OCaml ecosystems.
用途 | 一応汎用だが、 Web 志向 |
開始 | 2016/02 |
最新バージョン | 3.0.4 ( 2017/12/9 ) |
ライセンス | MIT |
プラットフォーム | JavaScript |
型 | 静的型付け |
パッケージ管理 | npm |
GitHubスター | 6902 ( 2018/12/02 ) |
- AltJS
- 高速なコンパイル
- BuckleScript が Reason / OCaml コードを JavasScript にコンパイルする
- リーダブルな JavaScript の生成
- 高速なコンパイル
- OCaml の Alternate Syntax
- OCaml の強力な型システム
- 関数型の恩恵
OCaml をベースに JSer にも親和性の高い Syntax が加えられた新しい言語みたいなイメージ。
インストール
- Windows 10 Pro 1803
$ npm install -g bs-platform
これだけで導入は完了。Windows でも問題なし。素晴らしい。
エディタ支援
VS Code が推奨されているので、素直にそれを使う。
これまでは、以下のプラグインを利用していたが、
https://marketplace.visualstudio.com/items?itemName=freebroccolo.reasonml
今後は公式に作られている以下プラグインの方が良いだろう。
https://marketplace.visualstudio.com/items?itemName=jaredly.reason-vscode
サンプル体験
コードの書き心地をするだけなら、Playground が用意されているが、実際の開発をイメージするためにローカルで始める。
プロジェクト作成
$ bsb -init sample_1206 -theme basic-reason
$ cd sample_1206
$ tree
# .
# ├── bsconfig.json
# ├── node_modules
# │ └── bs-platform -> /mnt/c/Users/username/scoop/persist/nodejs/bin/node_modules/bs-platform
# ├── package.json
# ├── README.md
# └── src
# └── Demo.re
コード
ちょっとコードを変更してみる。
let fizzbuzz = (i) =>
switch (i mod 3, i mod 5) {
| (0, 0) => "FizzBuzz"
| (0, _) => "Fizz"
| (_, 0) => "Buzz"
| _ => string_of_int(i)
}
for (i in 1 to 100) {
print_endline(fizzbuzz(i));
}
コンパイル
で、コンパイルすると、
$ npm run build
src/Demo.bs.js
が生成される。
// Generated by BUCKLESCRIPT VERSION 4.0.7, PLEASE EDIT WITH CARE
'use strict';
function fizzbuzz(i) {
var match = i % 3;
var match$1 = i % 5;
if (match !== 0) {
if (match$1 !== 0) {
return String(i);
} else {
return "Buzz";
}
} else if (match$1 !== 0) {
return "Fizz";
} else {
return "FizzBuzz";
}
}
for(var i = 1; i <= 100; ++i){
console.log(fizzbuzz(i));
}
exports.fizzbuzz = fizzbuzz;
/* Not a pure module */
ちなみに、package.json
は、
{
"name": "sample_1206",
"version": "0.1.0",
"scripts": {
"build": "bsb -make-world",
"start": "bsb -make-world -w",
"clean": "bsb -clean-world"
},
"keywords": [
"BuckleScript"
],
"author": "",
"license": "MIT",
"devDependencies": {
"bs-platform": "^4.0.7"
}
}
実行
Import
も無いただの JavaScript なので、普通に Node で実行する。
$ node .\src\Demo.bs.js
# 8
基本的には、これで終わり。
もし Webpack を使うなら、 loader は使わず BuckleScript の in-source
モードを使ってビルドとバンドルを分けるのが作法とのこと。Webpack と bs-platform の2つの Watcher を動かすのはちょっと面倒だけど。
Parcel を使うなら、全部解決してくれる。
1 年で進歩した点
前回の記事 : Reason 使った感想 on Windows
1. 公式推奨の VS Code 拡張が作られた
VS Code の拡張が新たに作られた。
https://marketplace.visualstudio.com/items?itemName=jaredly.reason-vscode
https://github.com/jaredly/reason-language-server
旧拡張 では、Windows での構築に相当手間取り、しかも最終的には上手く動かなかった。
新拡張はその点、導入もスムーズだ。特別な理由が無ければ、新拡張で良さそう。
ただ、モジュール変更 ~ 反映 までのタイムラグが結構あるのが気になる。まぁ、これは旧拡張でもあるもので、結局は WSL で BS を動かす辛さでしか無いのだが。
2. Belt という標準ライブラリ的モジュールの提供
Reason には、元々 OCaml の標準ライブラリ ( Pervasives ) をそのまま持ってきたみたいなモジュールがデフォルトで open
されていたが、もう少し Reason っぽい API の標準ライブラリ は無いものかと思っていた。
公式に Belt という標準ライブラリが提供されるようになった。
https://bucklescript.github.io/bucklescript/api/Belt.html
これは元々、便利機能を実装する予定だった BS モジュール が、名前を変えて実現したもののようだ。
例外の可能性のあるメソッド名に Exn
サフィックスが付く
let head = try(List.hd([])) {
| Failure("hd") => 10
};
open Belt;
let head = try(List.headExn([])) {
| Js.Exn.Error(_) => 10
};
例外の型まで変わっているのは気になる。JavaScript 側に相当寄せてきているんだろうか。
コレクション操作関数の引数の順番が変わった
let odds = List.map((x) => x * 2, [1, 3, 5, 10]);
open Belt;
let odds = List.map([1, 3, 5, 10], (x) => x * 2);
こうなると、気になるのが |>
の使い方で、あれはただ値を関数に渡しているだけだったが、位置が変わったら使えなくなる。
[1, 3, 5, 10]
|> List.map((x) => x * 2)
|> List.filter((x) => x > 7)
|> List.iter((x) => Js.log(x))
この場合 |>
は、 List.map
に与えられた関数が適用されて返された List => List
の関数に、[1,3,5,10]
を適用しているだけだ。順番が変わると使えない。
これに対しては、新たに ->
( Fast Pipe と呼ばれる ) が追加された。
こんな感じで使える。
open Belt;
[1, 3, 5, 10]
-> List.map((x) => x * 2)
-> List.keep((x) => x > 7)
-> List.forEach((x) => Js.log(x))
この場合、 パイプ元を第一引数に適用する。
また、|>
と一緒に _
プレースポルダーを置くことで任意の位置に値を適応することも可能。
open Belt;
[1, 3, 5, 10]
|> List.map(_, (x) => x * 2)
|> List.keep(_, (x) => x > 7)
|> List.forEach(_, (x) => Js.log(x))
この場合、関数のネストではなく、関数の演算結果が逐一変数に保存されるよな処理に置き換えられる。
よく使うデータ型の Generic Module の提供
module StringMap = Map.Make(String)
let m = StringMap.(
empty
|> add("key 1", 1)
|> add("key 2", 2)
)
open Belt;
let m = Map.String.fromArray([|("key 1", 1), ("key 2", 2)|])
Int
と String
のコレクションは提供されているようだ。
uncurried な Callback を受け取るメソッドには U
サフィックスが付く
open Belt;
/* コンパイルエラー */
[1, 3, 5, 10]
-> List.forEachU((x) => Js.log(x))
/* 正しくはこっち */
[1, 3, 5, 10]
-> List.forEachU((. x) => Js.log(x))
uncurried のほうがパフォーマンスが良いらしい。
uncurry についての詳細は こちら を参照。
3. boolean の扱いが改善した
これまでは、Reasonml の true
/ false
が JavaScript の 0
/ 1
に変換されていたため、思いもよらない動作をすることがあった。
その為、JavaScript と bool のやり取りをする時は Js.boolean
を使わなければいけなかった。
それがいつの間にか、JavaScript の bool に変換されるようになっていた。
小さいことだが、引っかかると結構なストレスだったので良かった。
4. セミコロンが強制ではなくなった
文末のセミコロンが強制ではなくなった。
未だにフォーマッタを利用すると文末にセミコロンは追加される。
思ったより進んでいなかった点
1. async/await の決着がついていない
ずーっと議論されている、async/await をどのような形で Reason に取り込むかという問題が、なかなかまとまらない。
https://github.com/facebook/reason/issues/1321
https://jaredforsyth.com/posts/building-async-await-in-reason/
2. 1年経っても Qiita のシンタックスハイライトが対応しない
まだまだ有名じゃないってことなんだろうか。
ちなみに、pony という言語の 『pony』タグは Qiita に 6 記事しか無いがシンタックスハイライトに対応している 。
それはさておき、どちらの言語にももっと広まってほしい。
参考
あとがき
※ この記事は個人の見解であり、所属する組織を代表するものではありません。