LoginSignup
3
1

More than 3 years have passed since last update.

If/Then/Maybe 〜条件分岐を Maybe で書き換える〜

Posted at

未来から来たジョン・タイターは次のように述べたらしい。

プログラミングの主流が、「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
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1