Edited at

値が制約を満たしていることを型で保証する

URLをあらわす文字列であるとか、0以上であることが要求される整数であるとか、値が何らかの制約を満たすことを要求される場面は多くあります。

たとえば、男女2頭のヤギさんの角の本数を引数にとって、その2頭のヤギさんが愛し合って交尾して子どもを作っても問題ないかを判断する関数を定義します。

canMakeLove : Int -> Int -> Bool

それぞれの角の数を引数にとって Bool型を返す自然な設計ですね?

でも、ちょっとまってください!

有角のヤギさんは2本しか角を持っていません。

そのため、ヤギさんのとりうる角の数は以下のいずれかしかありえないはずです。


  • 0本: 生まれつき無角のヤギさんか、有角の仔ヤギちゃんの角芽を焼きごてで焼いた(断末魔のような悲鳴がする)

  • 1本: 有角の仔ヤギちゃんの角芽を焼いたけど不十分で1つだけ残った(断末魔の悲鳴が無駄になった😱)

  • 2本: 生まれつき有角

  • 3本以上: 染色体に異常があるので、ここではヤギとは見なさない

つまり、canMakeLove の引数に 3以上の整数を与えると動作が保証されなくなってしまいます。

でも、関数を使う側が「気をつけて使う」なんて、現代のプログラミングではありえないですよね?

ちゃんと技術を使って解決しましょう!

sakura0.jpg


Horn型の導入

このように、何らかの制約を持つことが値に要求される場面でよく使われるテクニックがあります。

型によって値が制約を持つことを保証してしまうのです。

さっそくやってみましょう!

module Horn

exposing
( Horn
, fromInt
, toInt
)

{-| 角の本数をあらわす専用の型
-}

type Horn =
Horn Int

{-| `Horn` 型の値を作成する唯一の方法

fromInt 3
--> Nothing

fromInt 2
--> Just (Horn 2)
-}
fromInt : Int -> Maybe Horn
fromInt n =
if (0 <= n && n <= 2)
then
Just (Horn n)
else
Nothing

{-| `Horn` 型の値を整数に変換する唯一の方法
-}

toInt : Horn -> Int
toInt (Horn n) = n

{-| オスとメス(順不同)の角の本数から、その2頭が交尾しても問題ないかどうかどうかを判定する関数
-}

canMakeLove : Horn -> Horn -> Bool
canMakeLove = ...

まず注目していただきたいのは、HornOpaque type になっていることです。

module Horn

exposing
( Horn(..)
, ...

と記述すれば

Horn : Int -> Horn

というコンストラクタもこのモジュールの外で使えますが、ここでは Horn というのみを公開しているため、このコンストラクタはこのモジュール内部でしか使えません。

すると必然的に fromInt 以外に Horn型を作成する手段がありませんから、

Horn型の値は必ずヤギさんの角の本数としてValidである」と保証されます。

では、実際に canMakeLove 関数を作ってみましょう!

{-| オスとメス(順不同)の角の本数から、その2頭が交尾しても問題ないかどうかどうかを判定する関数

-}

canMakeLove : Horn -> Horn -> Bool
canMakeLove (Horn n) (Horn m) =
-- 無角同士の場合、無性のヤギが生まれてしまう可能性があるため交尾させない方が無難
not (n == 0 && m == 0)

このライブラリを使う際にはこんな感じになります。

import Horn exposing (Horn)

type alias Model =
{ horn1 : Int
, horn2 : Int
}

message : Model -> Html Msg
message model =
let
horn1 : Maybe Horn
horn1 = Horn.fromInt model.horn1

horn2 : Maybe Horn
horn2 = Horn.fromInt model.horn2

mresult : Maybe Bool
mresult = Maybe.map2 canMakeLove horn1 horn2
in
div
[]
[ text <|
case mresult of
Nothing ->
"入力値が不正です"

Just False ->
"無性のヤギが生まれる可能性があり、避けたほうが良いでしょう"

Just True ->
"ふたりの愛をはばむものは何もありません"
]

家族がふえるよ! やったね、ヤギさん

さくらちゃんにご飯をあげる

さくらちゃんをもっと見る

他の記事を見る

sakura.jpg