TL;DR

  • 単純型にも、桁数や範囲のような制約がある。
  • コンストラクタをプライベートにし、無効な値ならエラーを返す関数を用意して、不正な値を作れないようにする(スマートコンストラクタ)。

内容

単純型の制約

単純型にも制約はある。次の例では、制約をコメントで書いている。

type WidgetCode = WidgetCode of string // 先頭が "W" + 数字 4 桁
type UnitQuantity = UnitQuantity of int // 1 以上 1000 以下
type KilogramQuantity = KilogramQuantity of float // 0.05 以上 100.00 以下

しかし本当は、制約を満たさない値をそもそも作れないようにしたい。

スマートコンストラクタ

F# では、コンストラクタをプライベートにして別の関数を用意し、無効な値ならエラーを返すようにする。この方法をスマートコンストラクタと呼ぶ。エラーは Result で返す。

module UnitQuantity =
    type UnitQuantity = private UnitQuantity of int
 
    let create qty =
        if qty < 1 then
            Error "UnitQuantity can not be negative"
        else if qty > 1000 then
            Error "UnitQuantity can not be more than 1000"
        else
            Ok (UnitQuantity qty)
 
    // ラップされたデータを取り出す関数
    let value (UnitQuantity qty) = qty

使うときは次のようになる。

let unitQtyResult = UnitQuantity.create 1
 
match unitQtyResult with
| Error msg -> printfn "Failure, Message is %s" msg
| Ok uQty ->
    printfn "Success. Value is %A" uQty
    let innerValue = UnitQuantity.value uQty
    printfn "innerValue is %i" innerValue

キーワード

自分の理解

スマートコンストラクタは、F# 以外の言語でも再現できる。Dart での例は naoty さんの記事を参照。