世界を動かす技術を、日本語で。

OCamlを私の主要なプログラミング言語として

2025年8月14日原文(xvw.lol)

概要

  • 本記事は筆者の OCaml との出会いと、その 利点 についての意見を述べるもの
  • 言語自体・エコシステム・コミュニティの 三つの視点 から解説
  • インターネット上の 誤解や神話 の解消も試みる
  • 筆者は OCamlエコシステム で働いているが、以前から推進していた経緯を明記
  • 他の参考リソースも紹介し、OCamlの 多様な評価 を示す

OCamlとの出会いと推進理由

  • OCamlは 産業界研究分野 の両方で活用されるプログラミング言語
  • 研究に基づく 先進的な機能 (例:ユーザー定義エフェクト)と、実用性重視の 産業的機能 (例:アフィンセッション)を併せ持つ
  • Jane Street などの大手企業の導入実績があり、進化し続ける「生きた言語」としての側面
  • 理論専用Coq/Rocq実装専用 という誤解を払拭し、「表現力と安全性重視の産業向け関数型言語」として再評価
  • 筆者は学生時代から Site du Zéro のコミュニティでOCamlを知り、大学での体験はあくまで補足

他の参考リソース

  • 「Why OCaml?」: Real World OCaml の序章、OCamlの利点を事実ベースで解説
  • 「Better Programming Through OCaml」: OCaml Programming: Correct + Efficient + Beautiful の序章、OCaml習得が他言語スキル向上にも寄与
  • Yaron Minsky (Jane Street CTO)による講演「Why OCaml?」:産業導入の動機を深掘り
  • 「OCaml for Fun & Profit: An Experience Report」:生産現場での具体的活用事例
  • 「Replacing Python for 0Install」: 0Install 開発者による多言語比較とOCaml採用理由の詳細検証
  • 公式サイトには産業・学術両面の事例や、 OCamlへの不満 を示す記事も掲載

OCaml言語の特徴

  • MLファミリー の言語で、高レベル(ガベージコレクションあり)、静的型付け、型推論対応
  • 関数型命令型 の両スタイルが可能、 オブジェクト指向 や強力なモジュールシステムも提供
  • ocamlc (バイトコード)と ocamlopt (ネイティブコード)の二つのコンパイル方式
  • Js_of_ocamlMelange を用いたJavaScript・WebAssemblyへの変換で高い相互運用性
  • 多用途性 が高く、個人・業務プロジェクトの両方に最適なツール

静的型検査について

  • 個人プロジェクトでも 型安全性 の恩恵は大きく、ユニットテストのような「安全網」として機能
  • CやJava の型システムは制約が多いが、OCamlやHaskellのような型重視言語では「表現力豊かな設計ツール」として活用可能
  • 一度 高度な型システム に慣れると、単純な型や動的型への移行は困難
  • 歴史的にも多くの言語が 型検査の強化 を進めており、 TypeScriptMypy などの例がある
  • White House の公式レポートでもメモリ安全性の観点から Rust (OCamlが起源)を推奨し、型システムの重要性を強調
  • Tarides 社によるOCamlの安全性に関する主張も紹介

OCamlの長所と機能(抜粋)

  • 表現力の高い型システム による安全性と設計力
  • 関数型・命令型・オブジェクト指向 の柔軟な組み合わせ
  • 高速なコンパイラ と効率的な実行環境
  • 豊富なエコシステム と活発なコミュニティ
  • 産業界・研究分野両面 での導入実績

OCamlに関する誤解とその解消

  • 「理論専用」「学術向けのみ」といった誤解への反論
  • 産業界での 実用例 や、進化する言語仕様を根拠に説明
  • 一部の 不満点や課題 も認識し、適切な利用文脈を提案

まとめ

  • OCaml は多様な文脈で有効な選択肢
  • 静的型検査先進的な機能 を求めるなら特におすすめ
  • 他言語にも応用可能な設計思想
  • 完璧な技術は存在しない が、OCamlは十分に検討に値する選択肢

Hackerたちの意見

和集合型: 例えば、KotlinやJava(実質的にはC#)は、継承関係に関連する構造を使ってシーリングを行います。これにより、ケースを自分自身の型として参照できる利点があります。 > 和集合型の表現は冗長で、私の見解では考えにくいです。一度和集合型を宣言してしまえば、何度も使えます。少し冗長な和集合型の宣言でも、ケースの使い方がクリーンになるならそれだけの価値があります。

少し冗長な和集合型の宣言でも、ケースの使い方がクリーンになるならそれだけの価値がある。 その通り。ただ、JavaやKotlinの話をするときはそうじゃないよ。見た目が悪くて、JVM言語の典型的なボイラープレートが多いアプローチだね。

OCamlの具体的なケースでは、インデクシングやGADTs、ポリモーフィックバリアントを使うことでも可能だよ。でも一般的には、自分自身の型として参照することは異なる目的を持ってると思う。私の視点から見ると、和のブランチを区別することは、しばしば推論が難しく、バリアンスや型の等価性の喪失に関する懸念から一般化が難しいコードを生むことが多いね。

この利点は、ケースを独自の型として参照できるようになることだ。和集合型のケースは、もちろん型を持つ表現(いわゆる型コンストラクタの種類)だ。 datatype shape = Circle of real | Rectangle of real * real | Point Circle : real -> shape Rectangle : real * real -> shape Point : () -> shape ケース自体は型ではないけど、型は持っている。パターンマッチングのおかげで、和集合型のケースを扱うときに型コンストラクタのパラメータをすでに展開している。すべては宣言の局所性に関することだ。(real * real)はshapeの存在に依存しない。和集合型からケースを異なる型として取り出し始めると、網羅性を回避する能力が生まれ、和集合型は無効なプログラム状態を表現できなくなる。もう和集合型ではなくなる。名目上異なる型の和集合型がある場合、その和集合型はそれらの型の存在に依存している。クラス階層では、この関係が奇妙に逆転していて、影響が出る。 > 和集合型を一度宣言して、何度も使う。そして通常は多くの和集合型を書く。使い捨てだし、もっと言えば、自分が書いたコードを読む必要もある。ここでの冗長性のコストは過小評価されている。 > 少し冗長な和集合型の宣言は、ケースを使いやすくするために価値がある。C#/Javaには実際には和集合型がない。彼らの型システムとは互換性のない形式主義だ。とにかく、これらの例を見てみよう: C#: public abstract record Shape; public sealed record Circle(double Radius) : Shape; public sealed record Rectangle(double Width, double Height) : Shape; public sealed record Point() : Shape; double Area(Shape shape) => shape switch { Circle c => Math.PI * c.Radius * c.Radius, Rectangle r => r.Width * r.Height, Point => 0.0, _ => throw new ArgumentException("Unknown shape", nameof(shape)) }; ML: datatype shape = Circle of real | Rectangle of real * real | Point val result = case shape of Circle r => Math.pi * r * r | Rectangle (w, h) => w * h | Point => 0.0 ほとんど同じだよね、C#のOOPの奇妙さが邪魔してるけど。

OCamlでは働いたことはないけど、F#を少し触ったことがあって、なかなか楽しい経験だったよ。LLMの時代に、みんながもう一度関数型言語をじっくり見直すべきか考えてるんだ。私の考えでは、OCamlやHaskellなどのFP言語が多くの情報を少ないテキストに圧縮できるなら、コンテキストウィンドウにとっては良いことだと思う。もしかしたら、JavaやC#、Rubyなどの言語よりも、もっと密度の高いプログラムをモデルに入れて、一度に大きな変更を加えられるかもしれないね。

正直に言うと、今はLLMをドキュメントを書くのを手伝ったり(記事の翻訳も)、それだけに使ってるけど、他の人たちも注目してるみたいだね: https://anil.recoil.org/wiki?t=%23projects

これは本末転倒だと思う。プログラムは一般的に書くよりも読む方が難しいから、ツールのために簡潔な出力を最適化して人間にとってのコストを高めるのは、私個人としては選ばないトレードオフだね。確かに、これは特定のスタイルでコードを読む/書くのに慣れるための議論かもしれないけど、LLMの利点がなくても、関数型パラダイムやツールの採用は苦労してるよ。

C++とHaskellでシンプルなCLIゲームを書くっていう、完全に主観的な実験をしたんだけど、Haskellの方がコード行数は確かに少なかった。でも、単語数はほぼ同じで、つまりHaskellのコードは「高い」んじゃなくて「広い」って感じだった。しかも、この「実験」をJavaとか他のマネージドな命令型言語でやってないから、手動メモリ管理を気にしなくていい分、もっと軽くなったかもしれない。だから、どれくらい真実があるのかはわからないけど、プログラムによって違うと思う。命令型スタイルに向いてるものもあれば、関数型スタイルの方が合うものもあるよね。

LLMは、トレーニング例、静的型付け、LSPの実装から、簡潔さよりも恩恵を受けると思う。

大きなHaskellコードベースに取り組み始める前は、楽観的に考えてたんだけど、トレーニングコーパスにFPが足りないっていう明らかな問題を除けば、簡潔な言語はLLMとうまくいかないみたい。私の予想では、冗長さが生成を自己修正するのに役立つんじゃないかな。もし「悪い」トークンを予測したら、もっと柔軟に切り替えられて、ちゃんと動くコードを生み出せると思う。

私の考えでは、OCamlやHaskellのようなFP言語が多くの情報を少ないテキストに圧縮できるなら、それはコンテキストウィンドウにとって良いことだと思う。Claude CodeのHaskellスタイルは非常に冗長で、if-then-elseや多くのネストされたcase-of、複数の意図レベルでのdoブロックがあって、トップレベルでの命名がほとんどない。シンプルなAPIクライアントのサンプルを与えて、別のAPIでも同じことをするように頼んだら、すごくうまくいった。HaskellについてはJavaやRustよりも意見が多いと結論づけた。見た目が良くないなら、Haskellを使う意味がないよね。スタイルの例をコンテキストスペースをほとんど取らないように与えることができると思う。それに、.cabalにすでに入っている場合は、ファイルごとに言語プラグマを有効にしないようにリマインドするのもいいかも。

手続きは、関数型/ML構文ではもっと簡潔に書けるけど、そうじゃないことも多い。例えばC#の依存性注入は、素晴らしいDIライブラリと(より理にかなった)インスタンスコンストラクタ構文のおかげで、もっと冗長性が少なくできるんだ。

Hacker Newsで議論の続きを見る