Applicative型クラス
pureを実装した型クラス
定義
code:purs(hs)
class Apply f <= Applicative f where
pure :: forall a. a -> f a
Haskellの場合
code:hs
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
f aはここでは「ファンクター値」ではなく「アプリカティブ値」と呼ぶ
インスタンスとなる型
Maybe
code:hs
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
List
code:hs
instance Applicative [] where
Either
code:hs
instance Applicative Either a where
pure = Right
IO
code:hs
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
<*>では↓をやってるんだねmrsekut.icon
code:hs
-- このdo式全体の結果はIO 値
(IO 関数) <*> (IO 値) = do
関数 <- IO 関数
値 <- IO 値
pure (関数 値) -- ←これの結果がIO 値になる
(->) r
<*>を使っている例
code:hs
-- List
-- Maybe
Just (+5) <*> (Just 3)
-- IO
myAction :: IO String
myAction = do
a <- getLine
b <- getLine
return $ a ++ b
myAction = (++) <$> getLine <*> getLine -- 上と同じ
関連
Monadは条件分岐で、apは逐次実行
Applicativeは、Monadと異なり両辺に直接依存関係がないため、並列化しやすい
この説明は、以下のようなコードを見るとわかりやすい
code:hs
f a b = g
<$> h a
<*> h b
この時、h aとh bの結果は、最終的にgに適用されることになるが、
h aとh bの実行自体は並列で行うことができる
一方で、Monadの場合は、
code:hs
f a b = do
a' <- h a
b' <- h b
pure $ g a' b'
でありこれは、
code:hs
h a >>= (\a' ->
h b >>= (\b' ->
pure $ g a' b'
))
である
これは>>=の右辺が左辺に依存しているので、順番に実行しないといけない
この並列性の話と、「Applicativeは逐次実行」という説明の整合性が自分の中で取れていない #?? Monadのほうが逐次実行っぽくない?となっているmrsekut.icon
参考
Brent さんから Applicative スタイルを習ったのが分かって、微笑ましいです。 ref