OCaml
melangの各noteで他言語と比較した、OCamlの設計をまとめたページ。ここに書かれている内容は、各noteの比較表から自動で集めたものです。
型
Hindley-Milner型推論の実用化元となった言語で、型注釈はほぼ不要。
let age : int = 20let age : int = 20
let name = "Alice" (* 型注釈がなくても型推論で string と判定される *)Hindley-Milner系の型推論を実用言語として広めた源流で、型注釈はほとんどの場面で省略できる。バリアント型(代数的データ型複数の型を組み合わせて新しい型を作る手法。「AまたはB」のように取りうる形が閉じた集合になっているものをSum Type(直和型)と呼び、Rustの`enum`やHaskellの`data`が代表例。各variant(構成要素)が異なるフィールドを持てる点で、単純な整数の列挙とは異なる。)とパターンマッチが標準機能として組み込まれており、`option`型でNullの有無を型として表現する。RustやMoonBitの型システム・構文設計にも影響を与えている。
型注釈の位置
Haskellと同じくpostfix寄りだが、型注釈自体をほぼ省略できる。シグネチャを別ファイルに分離することもできる。
let age : int = 20let age : int = 20
let name = "Alice" (* 型注釈なしでも推論される *)
let greet (name : string) : string = "Hello, " ^ name
(* 型注釈をすべて省略しても推論できる *)
let greet name = "Hello, " ^ name変数・引数・戻り値のいずれも`: Type`という後置構文だが、Hindley-Milner推論により実務上はほとんど省略される。さらに`.ml`(実装)とは別に`.mli`(インターフェース)ファイルへ型シグネチャだけを分離して書くこともでき、Haskellの独立したシグネチャ行をファイル単位に発展させたような設計になっている。
型変換
変換ごとに関数を用意し、失敗し得るものに_optを付ける。
let y = Int64.of_int xlet x : int = 10
let y : int64 = Int64.of_int x
let f : float = float_of_int x
let a : int option = int_of_string_opt "42" (* Some 42 *)
let b : int = int_of_string "abc" (* Failure "int_of_string" *)`Int64.of_int`, `float_of_int`のように、変換元と変換先の組ごとに専用の関数を持つ。文字列のパースは例外を投げる`int_of_string`と、`option`を返す`int_of_string_opt`があり、名前の接尾辞で失敗の扱いを区別する。
暗黙の型変換
整数と浮動小数点の演算子すら分けるほど暗黙変換を持たない。
let x : int = 10let x : int = 10
let y : float = x (* 型エラー *)
let y : float = float_of_int x (* OK *)
let a = 1 + 2 (* int の加算 *)
let b = 1.0 +. 2.0 (* float の加算は別の演算子 *)数値型間の暗黙変換を一切持たない徹底した設計で、intとfloatの加算にも別の演算子(`+`と`+.`)を使い分ける必要があるほど厳格。変換には`float_of_int`, `int_of_float`のような専用関数を使う。
Enum / Sum Type
variant typeが標準機能としてpayloadを持てる。
type result = Success of user | Failure of stringtype result =
| Success of user
| Failure of string
let describe = function
| Success user -> "ok: " ^ user
| Failure message -> "error: " ^ message
(* パターン漏れはコンパイラ警告(non-exhaustive match)になる *)variant typeが標準機能で、コンストラクタごとに異なるデータを持てる。パターンマッチの網羅漏れはデフォルトで警告になり、`option`型もこの仕組みで表現される。
値の不変性
既定は不変だが、`ref`・`mutable`・配列で変更を明示的に使える。
let x = ... / let r = ref 0 / mutable fieldtype user = { name : string; mutable age : int }
let a = { name = "alice"; age = 20 }
let b = { a with name = "bob" } (* 新しい値 *)
(* a.name <- "x" コンパイルエラー: mutableではない *)
a.age <- 21 (* OK: mutableフィールド *)
let r = ref 0
let () = r := !r + 1 (* OK: refは変更できる *)
let arr = [| 1; 2 |]
let () = arr.(0) <- 9 (* OK: 配列は変更できる *)`let`束縛とレコードは既定で不変で、`with`で新しい値を作れる。ただし`ref`・`mutable`フィールド・配列・`Bytes`など、変更を使う手段が言語に含まれる。関数型が中心の言語だが、変更を明示して選べる。
名前付き引数
ラベル付き引数を言語機能として持ち、省略可能なoptional引数もある。
let foo ~width ~height = width + heightlet foo ~width ~height = width + height
foo ~width:10 ~height:20 (* ラベル名で明示 *)
foo ~height:20 ~width:10 (* ラベル付き同士は順序を入れ替えられる *)
let bar ~width ?(height=0) () = width + height
bar ~width:10 () (* heightは省略可能(デフォルト値) *)`~label:value`でラベル付き引数を、`?label`で省略可能な引数を表現できる。ラベル付き引数同士は呼び出し順序を入れ替えられ、ラベルなしの位置引数とも混在できる。
関数の引数
値は基本的に不変。書き換えたいときはref型の値を渡す。
let f (r : int ref) = r := !r + 1let f x = x + 1 (* 値渡し。xは不変 *)
let incr_ref (r : int ref) = r := !r + 1
let c = ref 0
let () = incr_ref c (* 呼び出し側は普通の値渡しに見える *)引数の渡し方を選ぶ構文はなく、書き換えたいときは`ref`型(可変セル)の値を渡す。渡し方ではなく、値の型で可変性を表す設計。呼び出し側は普通の値渡しと見た目が変わらないが、`ref`型の変数を渡していることは型を見れば分かる。可変なデータを持つ設計であり、melangでは採用しない。
if 文/式
`if`は式。`else`を省略できるのは、then側が`unit`のときだけ。
if cond then a else blet x = if cond then 1 else 2
(* elseを省略できるのはthen側がunitのとき *)
if cond then print_endline "yes"
(* let y = if cond then 1 型エラー *)`if`は式で、`else`を省略した場合はthen側が`unit`でなければならない。型システムが「値を返す`if`は`else`が必須」を保証する。
I/O の明示
通常の関数はI/Oを区別せず、effect handlerも型シグネチャには現れない。
let read_file : string -> stringlet read_file path = In_channel.read_all path
let square x =
ignore (read_file "x"); (* 呼べてしまう *)
x * xOCaml 5で導入されたeffect handler(`effect`構文)は、軽量スレッドや協調的並行処理を実装するための機構で、Haskellのeffect systemのような型レベルの追跡は持たない。ある関数がどのeffectを起こしうるかは、現状の型シグネチャからは読み取れない。型システムへのeffect tracking追加は研究段階にある。
言語名
"Objective Caml"の略称で、大文字小文字の表記揺れが長らく問題になった。
前身言語Caml(Categorical Abstract Machine Language)にオブジェクト指向機能を加えた"Objective Caml"の略称。正式表記は`OCaml`(先頭2文字のみ大文字、以降小文字)だが、`OCAML`や`Ocaml`といった表記揺れが普及初期から見られ、公式サイトが正しい綴りを案内する文書を用意するほどだった。略称化で名前は短くなった反面、大文字・小文字の並びという新たな表記ルールを覚える必要が生じた例。