Help us understand the problem. What is going on with this article?

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

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

プログラミングの主流が、「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
yumura_s
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした