LoginSignup
16
7

More than 5 years have passed since last update.

ReasonML雑感

Posted at
1 / 14

ReasonML

  • facebook製プログラミング言語
  • JavaScript、バイナリ両方にコンパイル可能
  • OCamlとJavaScriptのエコシステムが利用できる
  • 強力な型付けとイミュータブルが特徴
  • 2018/02/21 時点で最新バージョンは3.0.4

BuckleScript

  • OCamlのJavaScriptバックエンド
  • Bloomberg社製
  • ReasonMLはBuckleScriptに乗っかっている
  • 2018/02/21 時点で最新バージョンは2.2.0

アーキテクチャ

reason architecture
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みたいな構文なく、ファイル名をモジュール名として使ってアクセスできてしまう
16
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7