未来から来たジョン・タイターは次のように述べたらしい。
プログラミングの主流が、「If/Then」方式から「If/Then/Maybe」方式へと変わっている
よろしい、ならば条件分岐を Haskell のMaybe
で書き換えてやる!(ネタです)
If/Then/Else
次の条件分岐は
if flg then v1 else v2
下記のように書き換えられる。
guard flg $> v1 & fromMaybe v2
なお、全部 import が必要😇
import Control.Monad (guard)
import Data.Functor (($>))
import Data.Function ((&))
import Data.Maybe (fromMaybe)
仕組み
guard
guard の定義は次の通りだが、
guard :: (Alternative f) => Bool -> f ()
guard True = pure ()
guard False = empty
Maybe
の場合、必要な定義だけ参照すると
instance Applicative Maybe where
pure = Just
instance Alternative Maybe where
empty = Nothing
である。結局、Maybe
に特殊化したguard
は次のように動作する。
guard :: Bool -> Maybe ()
guard True = Just ()
guard False = Nothing
$>
$>
の必要な定義を抜き出すと次のようになる。
($>) :: Functor f => f a -> b -> f b
($>) = flip (<$)
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
class Functor f where
(<$) :: a -> f b -> f a
(<$) = fmap . const
fmap :: (a -> b) -> f a -> f b
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
const :: a -> b -> a
const x _ = x
以上より、次のように展開できる。つまり、$>
は既存の中身a
を捨てて、新規の中身b
に入れ替えるための演算子を意味する。
fa $> b
= flip (<$) fa b -- $> の定義
= b <$ fa -- flip の定義
= (fmap . const) b fa -- <$ の定義
= (\x -> fmap (const x)) b fa -- . の定義
= fmap (const b) fa -- 関数適用
= fmap (\_ -> b) fa -- constの定義と部分適用
Maybe
の場合、
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
だから、Maybe
に特殊化した$>
は次のように動作する。
($>) :: Maybe a -> b -> Maybe b
Just _ $> x = Just x
Nothing $> _ = Nothing
&
&の定義は次の通り。
(&) :: a -> (a -> b) -> b
x & f = f x
fromMaybe
fromMaybeの定義は次の通り。
fromMaybe :: a -> Maybe a -> a
fromMaybe d x = case x of {Nothing -> d; Just v -> v}
次のように見やすくしても同じ動作になる。
fromMaybe :: a -> Maybe a -> a
fromMaybe _ (Just v) = v
fromMaybe d Nothing = d
flg == True の場合
guard flg $> v1 & fromMaybe v2
= guard True $> v1 & fromMaybe v2 --仮定より
= Just () $> v1 & fromMaybe v2 --guardの動作
= Just v1 & fromMaybe v2 -- $> の動作
= fromMaybe v2 (Just v1) -- & の定義
= v1 -- fromMaybe の動作
flg == False の場合
guard flg $> v1 & fromMaybe v2
= guard False $> v1 & fromMaybe v2 --仮定より
= Nothing $> v1 & fromMaybe v2 --guardの動作
= Nothing & fromMaybe v2 -- $> の動作
= fromMaybe v2 Nothing -- & の定義
= v2 -- fromMaybe の動作
結論
以上より、次式が成り立つ。
guard flg $> v1 & fromMaybe v2
= if flg then v1 else v2
If/Then
余談だが、他の言語だとif(flg) return v;
のようにelse
の省略された早期リターン(ガード節)を書くことがある。ただ、Haskell ではelse
を省略できない。代わりに何も返さないことをNothing
で表現してみると、次のようになる。
if flg then Just v else Nothing
上記の条件分岐は If/Then/Else での考察を踏まえると、下記のように書き換えられる。
guard flg $> v