runFoo :: MonadReader (Foo m) m => m ()
Давайте забудем о классе и предположим, что MonadReader env mon
означает, что mon ~ ((->) env)
. Это соответствует простому использованию (->)
в качестве нашей монады вместо любителя ReaderT
. Тогда вы получите m ~ ((->) m) => m ()
. Вы видите, что m
должен содержать себя (в частности, аргумент m
равен m
). Это нормально для значений, но было бы неплохо, если бы контролер типов имел дело с бесконечно большими типами. То же самое верно для ReaderT
(и вам нужно использовать ReaderT
, потому что вы звоните runReaderT runFoo
). Вам нужно определить другой новый тип для кодирования этой рекурсии:
data RecReader c a = RecReader { runRecReader :: c (RecReader c) -> a }
instance Functor (RecReader c) where
fmap f (RecReader r) = RecReader $ f . r
instance Applicative (RecReader c) where
pure = RecReader . const
RecReader f <*> RecReader g = RecReader $ \e -> f e (g e)
instance Monad (RecReader c) where
return = pure
RecReader x >>= f = RecReader $ \e -> runRecReader (f (x e)) e
instance MonadReader (c (RecReader c)) (RecReader c) where
ask = RecReader id
local f (RecReader x) = RecReader $ x . f
И это работает:
runRecReader runFoo (Foo $ return ())
-- ==>
()