ArrowApplyはMonadと等価だという話を聞いたので、実際にArrowApplyからMonadを導出してみました。
標準ライブラリ中にはArrowMonadという型が定義されていますが、Arrow型の第一型引数が()
に束縛されています。
newtype ArrowMonad a b = ArrowMonad (a () b)
forall a. Arrow a => instance Functor (ArrowMonad a)
forall a. Arrow a => instance Applicative (ArrowMonad a)
forall a. ArrowApply a => instance Monad (ArrowMonad a)
今回はこの束縛をなくし、多相性を完全に保ったままMonadを導出します。
すなわち、
newtype ArrowMonadExt a b c =
ArrowMonadExt { runArrowMonadExt :: (a b c) }
forall a b. Arrow a => instance Functor (ArrowMonadExt a b)
forall a b. Arrow a => instance Applicative (ArrowMonadExt a b)
forall a b. ArrowApply a => instance Monad (ArrowMonadExt a b)
を定義します。ArrowMonadの時はインスタンス宣言の型変数が一つだった(forall a.
)のに対し、ArrowMonadExtの定義では型変数が二つになっている(forall a b.
)点がミソです。
また、クラス制約で示されているように、ArrowApplyが必要になるのはMonadの実装だけです。なので、普通のArrowもFunctorやApplicativeとしては扱えます。
共通ヘッダー
この記事のソースコードをコンパイルするには、各ソースコードの冒頭に以下を追加してください。
{-# LANGUAGE Arrows #-}
import Control.Arrow
ArrowMonadExtの定義
newtype ArrowMonadExt a b c =
ArrowMonadExt { runArrowMonadExt :: (a b c) }
Functorの定義
instance (Arrow a) => Functor (ArrowMonadExt a b) where
fmap f (ArrowMonadExt m) = ArrowMonadExt $
proc b -> do
c <- m -< b
returnA -< f c
Applicativeの定義
instance (Arrow a) => Applicative (ArrowMonadExt a b) where
pure x = ArrowMonadExt $
proc _ -> returnA -< x
(ArrowMonadExt l) <*> (ArrowMonadExt m) = ArrowMonadExt $
proc b -> do
f <- l -< b
c <- m -< b
returnA -< f c
Monadの定義
instance (ArrowApply a) => Monad (ArrowMonadExt a b) where
(ArrowMonadExt m) >>= k = ArrowMonadExt $
proc b -> do
c <- m -< b
d <- runArrowMonadExt $ k c -<< b
returnA -< d
P.S
多相性を完全に保ったままMonadを導出します。
と書きましたが、結局ArrowMonadExt a b
をMonadとして定義しているので、今回の定義とArrowMonadは本質的に同じです。つまり、事前にArrowの入力を()
として固定しているか、インスタンス定義時に型変数として固定するかの違いがあるだけで、やっていることは変わりません。