本書は OCaml のベテランプログラマ 2 名による解説本です. 第 2 版が 2021 年にかかれていて, ネットで無料公開されています.
全 3 章は以下のような構成で, OCaml 初心者でも読めますし, 深堀りされた解説からは経験者でも得るものがありそうです.

  1. OCaml の機能一通り (基礎文法, ヴァリアント, レコード, ファンクター, GADT, Class など)
  2. 具体例 (コマンドラインの引数パーズ, 非同期通信, json パーザ)
  3. OCaml のランタイムとコンパイラの仕組み

個人的にここ数か月 OCaml に興味を持って少しずつ触れてきたのですが, 入門の次に進むための知識が得たいと思い本書を読みました. 知りたかったことは例えば以下のような点です.

  • 標準ライブラリにはそれほど機能が揃っていないが, 便利なライブラリがあるのか (または自分でライブラリを整備するのが普通なのか)
  • プロジェクトをどのようにファイル構成するか
  • どの程度の規模でモジュールを分けるか
  • テストはどうやって書くか

こういった事柄はやや抽象的でそのものズバリ検索するのが難しいと思っていますが, 本書には知りたかったことは全て書いてありました. そのくらい網羅的で, もちろん知らなかったこと (知りたいとすら思わなかったこと) も満載でした.
高度な機能や具体的なライブラリの使い方などは読んでもピンとこなかったり軽く読み飛ばしたりした箇所があるのですが, 今後必要になったときに改めて読み返そうと思うような内容でした.

Base と Jane Street

本書では一貫して Base というライブラリが使われています. Base は OCaml の標準ライブラリを置き換えるべく作られたライブラリで, 今やデファクトスタンダードとなっている (らしい. 少なくとも私はそのような印象を受けた)OSS です.

メインの開発元は Jane Street という企業です. OCaml 界隈では存在感のある会社で, 株やオプションなどの取引市場を提供する金融×Tech の会社です. 複雑な金融商品を取り扱うためにテクノロジーに注力していて, その競争力の源泉となっているのが OCaml だということのようです. おそらく OCaml を使っている会社としては最も有名だと思います.

標準ではないライブラリに常に依存するのは若干抵抗がありますが, 非常に便利なので使えるなら使ったほうが良いと思います.
とはいえ, 実際にどの程度使われているのかはちゃんと調べていません. 歴戦の OCaml プログラマーは自作のライブラリを持っていると思うので, 不要だったりするのかもしれません.

すべてがリストだとこんなにも便利

本書で得た知識の中でも, 知っているのといないのとでは大違いだと思ったものにパイプ演算子 |> があります. 演算子の定義は以下の通りです.

1
let (|>) x f = f x

引数 関数 の順番を入れ替えて 関数 引数 とする演算子です. つまり, 以下の 2 つが同じになります.

1
2
let m1 = List.map [1; 2; 3] ~f:(fun x -> x * 2) (* [2; 4; 6] *)
let m2 = [1; 2; 3] |> List.map ~f:(fun x -> x * 2)

これだけだと一体なんの役に立つのかさっぱりですが, 例を見ると納得です. 0-99 の中から, 13 の倍数を抽出し, 7 で割ったあまりが大きい順に表示する例です (この例自体には特に意味はありません).

1
2
3
4
5
6
7
open Base

List.range 0 100
|> List.filter ~f:(fun x -> x % 13 = 0)
|> List.map ~f:(fun x -> x % 7)
|> List.sort ~compare:Int.compare
|> List.rev (* = reverse *)

この例のように, リストに対する処理をパイプで連鎖させることができます. ネストが深くならないので見やすいですし, あとから行やコメントを追加することも容易いです.
もし同じことをパイプ演算子なしでやると, 以下のようになります. あまりにも見辛いです.

1
2
3
4
5
List.rev
  (List.sort ~compare:Int.compare
     (List.map
        ~f:(fun x -> x % 7)
        (List.filter ~f:(fun x -> x % 13 = 0) (List.range 0 100))))

このパイプ演算子は Unix のパイプ (テキストを連鎖させる) や C# の Linq(IEnumerable を連鎖させる) と似ていると感じます. データ構造を統一するとこういう利点があると気付かされました.

OCaml のエコシステム

dune というビルドシステム (Rust の Cargo のようなもの. C/C++ の make の強化版), opam というパッケージマネージャ (gem, pip, npm のようなもの) があります.
モダンな言語には標準装備されているような気がしますが, やはりあると安心です.

ちなみに dune の設定ファイルには S 式を使います. さらに, デバッグなどのシリアライズ用にも S 式を使います.
json や XML ではなくて S 式を使うのは関数型っぽくて良いと思います. S 式は関数型世界の共通言語なのでしょうか.

1
2
3
- : Sexp.t = 5

- : string = "(1 2 3)"

凄そうだけどピンとこない機能

モジュール (Module) という便利な機能があります. これは, ある程度のコードをひとまとめにしてインターフェイスを定義できるもので, C++ だと Class のようなものです.

ファンクター(Functor) という, モジュールを引数にとってモジュールを返す関数のような機能があります. ごく簡単な例として, モジュールが持つ変数 x に 1 を足したモジュールを返すファンクターが紹介されています.

1
2
3
4
5
6
7
8
9
module Increment (M : X_int) : X_int = struct
  let x = M.x + 1
end

module Three = struct
  let x = 3
end

module Four = Incremnt(Three) (* Four.x = 4 *)

これは, 私にとって馴染みある C++ などの言語には相当するものがない機能だと思います. 強いて言うなら継承が近いでしょうか.
その他に, 端点を表すモジュールを引数にとって区間を扱うモジュールを返す例が紹介されています. これは確かに凄いですが, しかし他の使いみちをパッと思い浮かびません.

今まで触れたことがない概念であり, 抽象度も一段高いので理解しづらいのかなと思います. 読むだけだとピンとこないので, 実際にコードを色々書いてみて必要になったときに初めて理解できそうな気がします.
その他に, エラーハンドリングや Core.Async の非同期処理など, 読んですぐにはピンとこないものの必要になったときに再度参照したい項目がありました.

実践ワークショップ

本書とは別のものですが, Jane Street が提供している learn-ocaml-workshop というリポジトリがあります. OCaml の基礎文法を一通りテストできる演習問題がまとまっていておすすめです.

練習の一環としてスネークゲームを実装する課題がありました. 穴埋め形式でコードを書いてテストを通せば良いので取り組みやすかったのですが, ちゃんと分かった気がしなかったので, 改めて 自作しました.
なるべく依存が少なくなるようにして, 端末で動くシンプルなものを作りました. 実装にあたって curses/ncurses の OCaml バインディング curses を使わせていただきました.
スネークゲームのロジック自体はそこまで複雑ではありませんが, Core.Unix から低レベルな API を使った non-blocking キー入力や, Curses で未実装だった cbreak, noecho の実装が個人的な見どころです.

結び: OCaml の何に魅力を感じるか

正直なところ本書の内容を理解しきれておらず, 紹介できていない内容が多数あります (GADT など…). これらは今後 OCaml を使い続けていくうちに, いつか分かるときが来ると思っています. 再読して理解が深まったら加筆するかもしれません.

改めて, 何が面白くて OCaml に触れているのか考えてみると, C++ にはない概念や機能があるということが大きいと思います.

  • ファンクター, ヴァリアントなどは C++ にはない
  • 基本的にすべて immutable(C++ でもどうせほとんどの変数には const をつけるので, デフォルトが immutable な方が好き)
  • List が主要なデータ構造で, List を使った処理が上手く書ける
  • 強力なパターンマッチ
  • モジュールに型を定義して, 型の実装を非公開にしてインターフェイスを定義する (Sexp.t のように, 型の中身は分からない) ような流儀

今後 OCaml が流行るのか廃れるのかは分かりませんが, 少なくとも新しい見方や考え方を得られるのは, 損得抜きに純粋に楽しいです.
どれくらい OCaml に深入りするかは決めてませんが, 次はスネークゲームよりは大きいプログラムを作ろうと思っています.