TL;DR

ケースが 1 つだけの判別共用体。Domain Modeling Made Functional では、プリミティブな値を包むラッパー型として使い、値オブジェクトを表すのに役立つとしている。

なぜ必要か

Domain Modeling Made Functional によると、CustomerId と OrderId はどちらも int で表せても互換性はない。プリミティブな値を単純型で包むと型が別になり、誤って混同するとコンパイルエラーになる。

仕組み

F# では、ケース名をコンストラクタとして使って値を作る。F# のドキュメントによると、判別共用体はドメインモデリングで 1 つの型を包むのによく使われる。ケースが 1 つなら match 式を使わずに、パターンで中の値を取り出せる。関数のパラメータで直接取り出すこともできる。

例

type CustomerId = CustomerId of int
type OrderId = OrderId of int
 
let customerId = CustomerId 42
let (CustomerId innerValue) = customerId // 42
 
let processCustomerId (id: CustomerId) = ()
processCustomerId (OrderId 42) // コンパイルエラー

注意点

Domain Modeling Made Functional によると、単純型にはメモリと速度のコストがあるので、必要なら型エイリアスや構造体で代わりにする。また、単純型にも桁数や範囲のような制約があるので、コンストラクタをプライベートにして、無効な値を作れないようにする(スマートコンストラクタ)。

関連

出典