Boolの代わりにEither () ()を使って,論理演算をポイントフリースタイルで定義

Bool Either () ()
true Left
false Right

この記事では,上記の対応付けを使って,ポイントフリースタイルで論理演算を定義します。

否定 not の定義

a not a
true false
false true


notを定義するには,ArrowChoiceの(|||)を使います。

not :: Either () () -> Either () ()
not = false ||| true

true = Left, false = Rightであることに注意してください。


論理和 or を定義する準備

a b or a b
true true true
true false true
false true true
false false false


orの型は,以下のようになります。

or :: (Either () (), Either () ()) -> Either () ()

この記事では,複数個の引数は,タプルの形で与えます。カリー化はしません。



orを定義するために,補助関数distrを定義しておきます。distrは,orの第1引数を場合分けするために使います。

distr :: (Either a b, c) -> Either (a, c) (b, c)
distr = uncurry (curry Left ||| curry Right)
型の表記を変更する

型を読みやすくするために,型の表記を変更します。


distr :: (Either a b, c) -> Either (a, c) (b, c)

distr :: (a + b) * c -> a * c + b * c
or :: (Either () (), Either () ()) -> Either () ()

or :: )(() + ())( * )(() + ())( -> () + ()


orの型が「()」だらけで,まだ読みにくいので,さらに表記を変更します。

or :: )(() + ())( * )(() + ())( -> () + ()

or :: (T + F) * (T + F) -> T + F
Tはtrueに対応する型です。true = Leftなので,左側がTとなります。


orの定義

distrを使えば,orは以下のように定義できます。*1

or :: (T + F) * (T + F) -> T + F
or = distr >>> ((fst >>> true) ||| snd)


このときのdistrの役割は,こんな感じです。



おまけ:直積と直和の分配法則

distrには逆関数が存在します。

undistr :: a * c + b * c -> (a + b) * c
undistr = ((fst >>> Left) ||| (fst >>> Right)) &&& (snd ||| snd)


distrとundistrによって,直積と直和には分配法則が成り立つことがわかります。

(a + b) * c = a * c + b * c

*1:このorは短絡評価できます