generated at
検証済みであることを型で明示する
検証の前後で型を変えることで、その値の検証が済んでいるかどうかを確認できる
検証した後に、仕様を満たした型に変換すると良い
後続する関数は、「検証済みの型」を引数に取るように定義する

この話題をBooleanにのみに着目したものがBoolean blindness
Booleanは情報量として薄いので「検証の成功」を表すのに適切でない

何が嬉しいか?
検証作業の漏れが起きない
後続する関数を、「検証済みの型」を引数に取るように定義しておく
検証に成功していないと引数に渡せない
従って、検証することを強制でき、もし漏れていてもコンパイラが教えてくれる
型で意図を読み取れる
e.g. これは「ただのlistではなく、sort済みのlistを引数に取る」ことを想定して定義された関数ですよ、というのを利用者に伝えられる
実装ミスが減る
型に曖昧性が少なければ少ないほど、それに当てはまる実装が少なくなるので実装ミスは減る


ここで言う「検証」の例
文字列が空でないことを検証する
検証後に String ではなく NonEmpty String に変換する
リストをsortする
sort後に、 [a] ではなく SortedList a に変換する


userがloginしていることを検証する例 ref
if節でvalidationしてloginできるかどうかを判定している
hs
loginUser :: User -> Password -> PasswordHash -> IO Page loginUser user pwd pwdHash = if validateHash pwd pwdHash then getUserPage user else accessDenied user
validateHash の返り値は Bool
この検証の前後で user の型に変化がない
検証してようがしてまいが、同じものとして user を使えてしまう
だからelse節内で誤って getUserPage を呼ぶこともできてしまう
呼んだときに型エラーにならない
validateHash の実行を忘れて、直接 getUserPage user を呼ぶこともできてしまう
検証後に UserAuth という型に変換するように書き換えると良い
hs
data UserAuth validateHash :: Password -> PasswordHash -> Maybe UserAuth validateHash = undefined getUserPage :: UserAuth -> User -> IO Page getUserPage = undefined loginUser :: User -> Password -> PasswordHash -> IO Page loginUser user pwd pwdHash = case validateHash pwd pwdHash of Nothing -> accessDenied user Just auth -> getUserPage auth user
検証後に型を変える
getUserPage は検証済みのuser( UserAuth )のみを引数に取るようにする
検証忘れが起きない
書き換え前のthen節とelse節を逆に書くようなこともできなくなる


Bool を返す関数で判定することで文脈が損失する例 ref
hs
add :: (a -> Maybe Int) -> (a -> Maybe Int) -> a -> Maybe Int add f g x = if isNothing (f x) || isNothing (g x) then Nothing else Just (fromJust (f x) + fromJust (g x))
f , g は検証を行うような関数
isNothing という Bool を返す関数で判定しているため、検証の情報が伝達していない
if節とelse節の両方で f x を実行している
本来なら f x を1回実行すれば Just Nothing かの情報が乗るので、それを上手く使えるはず
then 節と else 節の中身を入れ替えても型エラーにならない
こう書き直せばいい
hs
add :: (a -> Maybe Int) -> (a -> Maybe Int) -> a -> Maybe Int add f g x = do a <- f x b <- g x pure $ a + b



filter を例にとった説明 ref
filter :: (a -> Bool) -> [a] -> [a]
デフォルトの filter は判定する関数がBoolなので分かりづらい
意図を真逆に解釈できてしまう
trueのものを残すのか、falseのものを残すのか、型から推測できない
ではどうするか?
より明示的な型でやる
1.hs
data Keep = Discard | Keep filter1 :: (a -> Keep) -> [a] -> [a]
maybeを使う
3.hs
filter3 :: (a -> Maybe b) -> [a] -> [b]


素数判定を行った後に、それを明示する型に変換する例 ref
hs
newtype Prime = Prime Int prime :: Int -> Maybe Prime





キーワードとしてはこの辺も近いっぽい