混乱の原因は、
class Monad m => MonadReader r m | m -> r where
{- ... -}
自身をm
含むことが暗黙的に想定されていr
ます(一般的なインスタンスの場合)。私は軽いのdefiniton使用してみましょうReader
としてを
newtype Reader r a = Reader {runReader :: r -> a}
ときにr
パラメータが選択されますがeaselyのためのモナドのインスタンスを定義することができますReader r
。つまり、型クラスの定義m
はの代わりに使用する必要がありますReader r
。だから、表現が最終的にどのようになるかを見てみましょう:
instance MonadReader r (Reader r) where -- hey!! r is duplicated now
{- ... -} -- The functional dependecy becomes Reader r -> r which makes sense
しかし、なぜこれが必要なのでしょうか。クラスask
内のの定義を見てくださいMonadReader
。
class Monad m => MonadReader r m | m -> r where
ask :: m r -- r and m are polymorphic here
{- ... -}
fun-depがなければ、ask
別のタイプを状態として返すように定義するために私を止めることはできません。さらに、自分のタイプのモナドリーダーの多くのインスタンスを定義できます。例として、これはfunc-depなしの有効な定義になります
instance MonadReader Bool (Reader r) where
-- ^^^^ ^
-- | |- This is state type in the user defined newtype
-- |- this is the state type in the type class definition
ask :: Reader r Bool
ask = Reader (\_ -> True) -- the function that returns True constantly
{- ... -}
instance MonadReader String (Reader r) where
-- ^^^^^^ ^
-- | |- This is read-state type in the user defined newtype
-- |- this is the read-state type in the type class definition
ask :: Reader r String
ask = Reader (\_ -> "ThisIsBroken") -- the function that returns "ThisIsBroken" constantly
{- ... -}
したがって、もし私が値を持っていたらval :: ReaderT Int IO Double
、結果はどうなるでしょうask
。以下のように型シグネチャを指定する必要があります
val :: Reader Int Double
val = do
r <- ask :: Reader Int String
liftIO $ putStrLn r -- Just imagine you can use liftIO
return 1.0
> val `runReader` 1
"ThisIsBroken"
1.0
val :: Reader Int Double
val = do
r <- ask :: Reader Int Bool
liftIO $ print r -- Just imagine you can use liftIO
return 1.0
> val `runReader` 1
True
1.0
意味がないことは別として、型を何度も指定するのは不便です。
の実際の定義を使用した結論としてReaderT
。あなたが何か持っている場合はval :: ReaderT String IO Int
、機能の依存関係を言うようなタイプがのただ1つのインスタンスかもしれないMonadReader
型クラスを使用していることを一つであると定義されているString
として、r