OCaml Weekly News

先週号 上へ 次週号

こんにちは

2026年6月23日から30日までの週の OCaml Weekly News をお届けします。

Table of Contents

Sek の新リリース

François Pottier が発表しました

効率的な ephemeral(ミュータブル)および persistent(イミュータブル)な シーケンス データ構造を提供し、これら二つの形式間の効率的な変換もサポートするライブラリ Sek の新リリースを発表できることを嬉しく思います。

この新リリースでは、ランダムアクセス(getset)、分割、連結など複数の操作を高速化する最適化が導入されています。詳細については 変更履歴 をご覧ください。

opam update && opam install sek

Happy hacking, Arthur Charguéraud & François Pottier.

François Pottier が後に追記しました

ドキュメント へのリンクも追加させてください。

Js_of_ocaml / Wasm_of_ocaml 6.4

Hhugo が発表しました

js_of_ocaml および wasm_of_ocaml 6.4.0 の同時リリースを発表できることを嬉しく思います。

Js_of_ocaml は OCaml バイトコードから JavaScript へのコンパイラです。ブラウザや Node.js などの JavaScript 環境で純粋な OCaml プログラムを実行できます。

Wasm_of_ocaml は OCaml バイトコードから WebAssembly へのコンパイラです。js_of_ocaml との高い互換性を持ち、js_of_ocaml の代わりに wasm_of_ocaml でプログラムをコンパイルすることで全体的なパフォーマンス向上が期待できます。

バージョン 6.3 以降の主な変更点:

ツールチェーン

  • OCaml 5.5.0 サポート
  • OxCaml サポート

wasm_of_ocaml

  • WASI 0.1 サポート — スタンドアロンの WASI ランタイム(wasmtime など)をターゲットにでき、JavaScript ホストは不要。
  • Dynlink とトップレベルのサポート — OCaml トップレベルが Wasm バックエンドで動作するようになりました。
  • Stack Switching proposal に基づく 代替 effects バックエンド
  • Pure-Wasm の zstd と BLAKE2b — 圧縮された値のアンマーシャリングと Digest.BLAKE2{512,256,128} で JavaScript shim が不要になり、WASI でも動作します。
  • レガシーの num ライブラリが Wasm で動作するようになりました(nat プリミティブがノーオプスタブでした)。

ライブラリ — 新しい web API

  • Promise モジュール — JS Promise への型安全なバインディング。Lwt 相互運用Js_of_ocaml_lwt.Promise.{to_lwt,of_lwt})および Promise 型の Dom_html バインディング付き。
  • Fetch + Abort モジュール — 型付き AbortController~/~AbortSignal キャンセル機能を持つ Fetch API。
  • SVG 2 に合わせた Dom_svgpopover APIIntl.RelativeTimeFormat、新しい Performance モジュール、追加の Console バインディング、その他多数の Dom_html バインディング。

バグ修正

  • コンパイラ、ランタイム、ライブラリバインディング、PPX にわたる多数のバグ修正。

⚠️ 破壊的変更

一部の変更はソース非互換です — 主に誤ったバインディングと動作を修正するためのもの — で、既存コードの更新が必要になる場合があります。移行ガイドを参照してください。

ドキュメント および 完全な変更履歴 もご覧ください。

Vincent Balat が続けて追記しました

同時に Eliom 12.1 もリリースしました。js_of_ocaml 6.4 と OCaml 5.5 の両方との互換性を提供します。

Ocsipersist 2.1.0: Deriving を使った型安全な永続参照

Vincent Balat が発表しました

Ocsipersist 2.1.0 がリリースされました。永続参照、ストア、テーブルを Marshal の代わりに Deriving_Json でシリアライズできるようになりました。データはデータベース内で人間が読める形式になり、OCaml バージョン間でも安定しています。新しい API は完全に後方互換性があり、Ocsipersist は任意の OCaml プログラムのスタンドアロンライブラリとして動作します(Eliom や Ocsigen Server は不要です)。

type user = { name : string; age : int } [@@deriving json]

(* 読み取り可能な JSON として保存される永続参照 *)
let visits = Ocsipersist.Ref_json.ref ~persistent:"visits" [%json: int] 0

let count_visit () =
  let%lwt n = Ocsipersist.Ref_json.get visits in
  Ocsipersist.Ref_json.set visits (n + 1)

完全なアナウンス: https://ocsigen.org/blog/posts/ocsipersist-2.1.0.html

OCaml 5.5 以前における modular explicits

oleg が述べました

先日発表された OCaml 5.5 の注目すべき機能の一つが、モジュール依存関数、すなわち modular explicits です。これは歓迎すべき追加機能で、最もシンプルな方法で higher-ranked 型を表現でき、型付き tagless-final コードに特に適しています。

しかしながら、modular explicits はそれ以前でも、ほとんど手間なく実現できたことを述べておくべきでしょう。この古いトリックを改めて紹介することには意義があると思います — 特に、OCaml 5.5 の modular explicits の対象外である静的に未知なモジュール(statically unknown modules)でも動作するからです。

実例として、OCaml 5.5 の発表(以下 Ann55)にあるプリティプリンティングの例を再利用します。

ただし、より単純な例から始めます: Set.Make functor で生成されたセットのプリティプリンティングです。この例は Ann55 の pp_map 関数のパターンに従っています。マップの表示と異なり higher-rank 型が含まれないため、より単純です。

let pp_set (type a s) 
      (module M: Set.S with type elt = a and type t = s) 
      (pp_elt:Format.formatter->a->unit) (ppf:Format.formatter) (set:s) = 
  if M.is_empty set then Format.fprintf ppf "ø" else 
  let pp_sep ppf () = Format.fprintf ppf ",@ " in 
  Format.fprintf ppf "@[{@ %a@ }@]" 
    (Format.pp_print_seq ~pp_sep pp_elt) (M.to_seq set) 

これは Ann55 の pp_map の例をほぼそのままマップをセットに置き換えたものです。主な違いは型アノテーションです。これはバグではありません: 私はすべてのトップレベル定義(最も自明なものを除いて)にシグネチャや明示的な型アノテーションを書くことを強く推奨しています。

以下を導入することでアノテーションを簡略化できます:

type ('e,'s) set = (module Set.S with type elt = 'e and type t = 's)
type 'a printer = Format.formatter->'a->unit

例はこのようになります:

let pp_set : type e s. (e,s) set -> e printer -> s printer = 
  fun (module M) pp_elt ppf set -> (* ... 前と同様 ... *)

シグネチャを見れば pp_set が何をしているかが一目瞭然です(シグネチャの利点の一つです: コンパイラのためだけでも、主にコンパイラのためでもありません)。

pp_set は Ann55 の例での pp_map の使われ方と同様に使えます:

module String_set = Set.Make(String) 

let () = 
  let m = String_set.of_list ["Zero"; "Zero"; "One"; "Un"] in 
  let pp_str = Format.pp_print_string in 
  Format.printf "%a@." (pp_set (module String_set) pp_str) m 

私たちの modular explicits の表現は、String_set のような静的に既知なモジュールを超えて拡張できます。例えば:

(* 型 'e の要素を持つ抽象集合。実装は抽象的 *)

type 'e aset = (module Set.S with type elt = 'e)

let f : unit -> int aset = fun () -> 
  if Random.bool () then 
    (module Set.Make(Int)) 
  else 
    (module Set.Make(struct type t = int 
      let compare x y = - Int.compare x y end))

関数 f はランダムに二つの異なる Set 実装のいずれかを返します(Set.t 型には互換性がありません)。応用として、整数のリストをセットとして表示します(自動的にソートおよび重複削除されます):

let print_as_set : type a. a aset -> a printer -> a list printer  = 
  fun (module M) pp ppf lst -> 
    pp_set (module M) pp ppf (M.of_list lst)

let _ = Format.printf "%a@." 
    (print_as_set (f ()) Format.pp_print_int) [1;2;3;1]

結果は、サイコロの目に応じて "{ 1, 2, 3 }" または "{ 3, 2, 1 }" のいずれかになります。

次に Ann55 の pp_map の例に取り組みましょう。これはマップ型 'a Map.S.t の higher-rank 型のために難しい問題です。

モジュール型を次のように定義したくなります:

type ('k,'v,'m) map = 
    (module Map.S with type key = 'k and type 'a t = 'm constraint 'a = 'v)

残念ながら、これは動作しません:

2 |     (module Map.S with type key = 'k and type 'a t = 'm constraint 'a = 'v)
                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Syntax error: invalid package type: parametrized types are not supported

パッケージ型とその with 制約には多くの制限があり、非常に残念です。

エンコーディングに頼る必要があります:

module type maps = sig
  include Map.S
  type v
  type mt
  val of_mt : mt -> v t
  val to_mt : v t -> mt 
end
type ('k,'v,'m) map = 
    (module maps with type key = 'k and type v = 'v and type mt = 'm)

これはより一般的なマップ型であることを述べておくべきでしょう: キーと値の特定の組み合わせに対する特殊な実装をサポートします(例: 'v が bool の場合、基礎構造として Set を使用できます)。

マップのプリティプリンターは Ann55 と同様で、M.of_t の二つの出現が追加されています:

let pp_map : type k v m. (k,v,m) map -> k printer -> v printer -> m printer = 
    fun (module M) pp_key pp_v ppf set -> 
  if M.is_empty (M.of_mt set) then Format.fprintf ppf "ø" else 
  let pp_sep ppf () = Format.fprintf ppf ",@ " in 
  let pp_binding ppf (k,v) = Format.fprintf ppf "@[%a@ =@ %a@]" pp_key k pp_v v 
  in 
  Format.fprintf ppf "@[{@ %a@ }@]" 
    (Format.pp_print_seq ~pp_sep pp_binding) (M.to_seq (M.of_mt set)) 

ただし Ann55 の pp_map とは異なり、静的に未知なモジュールにも適用できます:

type ('k,'v) amap = (module maps with type key = 'k and type v = 'v)

let string_map : type a. unit -> (string,a) amap = fun () -> 
  (module struct
    include Map.Make(String) 
    type v = a
    type mt = v t
    let of_mt = Fun.id
    let to_mt = Fun.id
  end)

let () = 
  let md = string_map () in
  let module M = (val (md : (string,int) amap)) in
  let m = M.of_list ["Zero", 0; "One", 1] in 
  let pp_str = Format.pp_print_int in 
  Format.printf "%a@." 
    (pp_map (module M) Format.pp_print_string pp_str) (M.to_mt m)

to_mt のような関数は tagless-final インタープリタの場合に自然に現れることを述べておきましょう: これらは観察関数です。例えば:

module type lc = sig
  type 'a repr
  val int : int -> int repr
  val lam  : ('a repr -> 'b repr) -> ('a -> 'b) repr
  val app : ('a -> 'b) repr -> ('a repr -> 'b repr)
  type obst
  type obs
  val observe : obst repr -> obs
end

type ('a,'obs) lc = (module (lc with type obs = 'obs and type obst = 'a))

let ex1 : type obs. (int,obs) lc -> obs = fun (module M) -> let open M in
  let t1 = app (lam (fun x -> x)) (int 1)
  in observe t1

結論として、パッケージ型の制限がいつか緩和されることを願っています。

Olivier Nicole が質問し、Samuel Vivien が回答しました

ご指摘ありがとうございます。modular explicits は厳密に言えば言語に表現力を追加するものではなく、これらのことをより簡単に行うための方法を提供するだけということでしょうか?それとも modular explicits で表現できるが、制約付きモジュール型では表現できないプログラムが存在しますか?

その通りです。Modular explicit は言語に表現力を追加しません(型システムの健全性にも影響しません)。modular explicits を使って書けるプログラムは、すべて第一級モジュールとしてエンコードされた functor を使って書くことができたはずです。

このエンコーディングについては、modular explicits に関するこの論文のセクション 1.5 で説明しています: https://hal.science/hal-05428136/document

LRgrep 0.9: OCaml と Menhir のためのより良い構文エラー

Frédéric Bour が発表しました

皆さん、こんにちは。

LRgrep 0.9 をリリースしました。これは、失敗パスを推論することで Menhir が生成した LR パーサのエラーメッセージをカスタマイズするツールです。

目標は、汎用的な「X行目で構文エラー」というメッセージから、実際の根本原因を指摘するより直感的な診断メッセージへの移行です。例えば、ローカル let チェーンに誤ってセミコロンを置いてしまった場合:

let x = 5;

let y = 6

let z = 7
^^^
Error: Expecting 'in' to complete local-let binding. 
Hint: this might be due to the semicolon line 1, character 9

LRgrep なしでは、OCaml は通常、後続の let バインディングに対してのみ構文エラーを報告します。カスタマイズされたパーサにより、セミコロンが原因である可能性を特定できます。

フィードバックをお願いしています!

現在の仕様のストレステストを行い、エラーメッセージがまだ不十分な箇所を特定するためにユーザーの協力を求めています。

  1. OCaml をお使いの方: コンパイラに LRgrep パッチを当てています。opam でお試しください: opam switch create 5.4.1+lrgrep エラーメッセージが分かりにくい場合や、より有益なヒントが欲しいと感じた場合はご報告ください。
  2. Menhir をお使いの方: 独自のグラマーにエラーメッセージをカスタマイズしたい場合は LRgrep をお試しください。

Merlin との統合も近日公開予定です(PR #2072)。

@fpottier、@jmid、Tarides、Jane Street のサポートに深く感謝します。

Happy testing!

OCaml の静的解析

fantazio が発表しました

皆さん、こんにちは。

OCaml の静的解析ツールの現状について短い(やや独断的な)記事を書きました。 利用可能なツール、開発中の取り組み、不足している部分の概要を提供することを目的としています。言及されていないがご使用の静的解析ツール、または存在してほしいと思うものがあればお知らせください。

ありがとうございます!

https://fantazio.eu/articles/static_analysis_ocaml.html

新リリース: OCaml 5.5 サポートを含む Merlin 5.8 と OCaml-LSP 1.27.0

vds が発表しました

Merlin と OCaml-LSP の新リリースを発表できることを嬉しく思います 🧙 !

Merlin は OCaml 向けにモダンな IDE 機能を提供するエディターサービスです。OCaml-LSP は LSP クライアントと通信する Merlin のフロントエンドです。

これらのリリースにはいくつかのバグ修正が含まれていますが、主な機能は OCaml 5.5 の安定サポートです。

完全な変更履歴:

duras 2.1.1 — プレーンテキストファイルとしての日々のノート

Sergiy Duras が発表しました

duras は、プレーンテキストファイル(YYYY/MM/YYYY-MM-DD.dn)として保存される日々のノートのためのコマンドラインツールです。データベースなし、デーモンなし、バックグラウンドサービスなし。

v2.1.1 では duras build が追加され、ノートコレクションを静的 HTML ウェブサイトに変換します。7,300 件のノートを約220ms で処理、シングルスレッド、キャッシュなし。ノートは sigline フォーマットを使用します — 各行の先頭文字でその行の型を宣言するプレーンテキスト構造記法です。

LexiFi(パリ)のソフトウェアエンジニア募集

Nicolas Ojeda Bar が発表しました

皆さん、

LexiFi では採用を行っています!コア開発チームに参加するフルタイムのソフトウェアエンジニアを募集しています。

前職での業界経験、オープンソースプロジェクトへの貢献、実質的な個人プロジェクトなど、強力なプログラミングバックグラウンドを持つ候補者を特に求めています。

LexiFi は OCaml を 25 年以上使用しており — OCaml で製品を構築した最初のソフトウェア会社です — 現在もスタックの大部分を OCaml で実装しています。OCaml を使って実世界の産業問題を解決することに興味がある方は、ぜひご連絡ください。

https://www.lexifi.com/careers/software_engineer/

LexiFi での業務は複数のドメインにわたる幅広いプロジェクトに及びます。また、コミュニティプロジェクトへの資金提供、OCaml コンパイラやその他のオープンソースプロジェクトのメンテナンスへの協力、カンファレンスへの参加など、様々な形で OCaml コミュニティへの貢献にも積極的に取り組んでいます。

ご質問がございましたら、こちらまたは個別にお気軽にご連絡ください。

よろしくお願いします、 Nicolas

Owebview: webview ライブラリへの OCaml バインディング

Korkorran が発表しました

皆さん、こんにちは!👋

取り組んできた小さなプロジェクトをご紹介します: owebviewwebview のための OCaml バインディングセットです。

webview とは?

webview は、OS 組み込みの web エンジン(macOS では WebKit、Linux では WebKitGTK、Windows では WebView2)を使ってデスクトップ GUI を構築するための小さなクロスプラットフォームライブラリです。Electron のようにブラウザ全体を同梱する代わりにシステムのものを再利用するので、アプリを小さく保てます。ウィンドウを作成し、HTML(または URL)を指定すると、ページの JavaScript とホスト言語の間で相互に呼び出しができます。

owebview が埋めるギャップ

webview が非常に魅力的なのはそのエコシステムです: Go(リファレンス実装)、Rust、Python、C#、Nim、Zig など、すでに多くの言語向けバインディングがあります。しかし、私の調べた限り、OCaml 向けのものはありませんでした。

owebview はそのギャップを埋める試みです: OCaml から直接 webview を操作し、ページの JavaScript と OCaml 関数間のネイティブブリッジを提供する薄いバインディングです。

どのようなものか

完全なアプリは約10行です:

let () =
  let w = Webview.create () in
  Webview.set_title w "My first owebview app";
  Webview.set_size w \~width:480 \~height:320 Webview.Hint_none;
  Webview.set_html w
  {|<!doctype html>
      <html><body style="font-family: system-ui; text-align: center">
        <h1>Hello from OCaml 👋</h1>
      </body></html>|};
  Webview.run w;
  Webview.destroy w

ページに OCaml 関数を公開することもできます — ここでは window.add(a, b) が OCaml から解決される JavaScript Promise を返します:

Webview.bind w "add" (fun id req ->
  let result =
  match Scanf.sscanf_opt req "\[%d,%d\]" (fun a b -> a + b) with
  | Some n -> string_of_int n
  | None -> "null"
  in
  Webview.return w id \~error:false \~result)

試してみる

# バンドルされた例を実行
git clone https://github.com/korkorran/owebview.git
cd owebview
dune exec examples/hellowv.exe

# または独自プロジェクトに pin する
opam pin add owebview https://github.com/korkorran/owebview.git

その後、dune ファイルに (libraries owebview.webview) を追加するだけです。webview.h ヘッダーはベンダリングされており、プラットフォーム固有の C++ フラグはビルド時に検出されます(Linux では pkg-config を使用)ので、手動での設定は不要です。

率直な現状とお願い

これはコンパクトなバインディング / 出発点であり、まだ完全なライブラリではありません: 一部の機能(unbinddispatch、完全なバインディングメモリ管理)は現時点では意図的に省略されています。

また、主に macOS で開発・テストされています。Linux ディストリビューションで実行されている方からのフィードバックをぜひいただきたいです — コンパイルできますか?opam の depexts は解決しますか?お使いのディストリビューションで webkit2gtk-4.1 バックエンドは動作しますか?

Issue や PR は大歓迎です。

リポジトリ: https://github.com/korkorran/owebview

お読みいただきありがとうございます。感想、提案、そして特に Linux ビルドの報告をお待ちしています!

OCaml Runtime ミーティング: 7月6日(月)UTC 10:00(ロンドン/ケンブリッジ 10:00、パリ 11:00、シドニー 19:00)

Tim McGilchrist が発表しました

次の OCaml Runtime ミーティングは 2026年7月6日(月)UTC 10:00(ロンドン/ケンブリッジ 10:00、パリ 11:00、シドニー 19:00)に開催されます。アジェンダのノートはこちらです https://hackmd.io/@tmcgilchrist/S1RtObvWGe カレンダー招待が必要な場合は tim@tarides.com にメールしてください。

おおよそ毎月、言語ランタイム/ガベージコレクターに焦点を当てた OCaml 開発者間の非公式ミーティングを開催しています。ミーティングは理解やアイデアを共有するためのもので、正式な計画を目的としていません。興味のある開発者はどなたでも参加できます。

過去のミーティングノートは https://github.com/ocaml/subsystem-meetings でご覧いただけます。

過去の CWN

CWN を見逃した場合は、メッセージを送っていただければメールでお送りします。また、アーカイブRSS フィードもご覧いただけます。

毎週メールで受け取りたい場合は、caml-list を購読してください。