Ich denke, die Quelle der Verwirrung ist die in der Definition von
class Monad m => MonadReader r m | m -> r where
{- ... -}
Es wird implizit angenommen, dass es sich selbst menthält r(für häufige Fälle). Lassen Sie mich eine leichtere Definition von Readeras verwenden
newtype Reader r a = Reader {runReader :: r -> a}
Wenn der rParameter ausgewählt ist, können Sie einfach eine Monadeninstanz für definieren Reader r. Das bedeutet, dass in der Typklasse die Definition ersetzt werden msollte Reader r. Schauen Sie sich also an, wie der Ausdruck endet:
instance MonadReader r (Reader r) where -- hey!! r is duplicated now
{- ... -} -- The functional dependecy becomes Reader r -> r which makes sense
Aber warum brauchen wir das? Schauen Sie sich die Definition askinnerhalb der MonadReaderKlasse an.
class Monad m => MonadReader r m | m -> r where
ask :: m r -- r and m are polymorphic here
{- ... -}
Ohne die fun-dep könnte mich nichts daran hindern, askeinen anderen Typ als Zustand zurückzugeben. Darüber hinaus konnte ich viele Instanzen von Monadenlesern für meinen Typ definieren. Dies wären beispielsweise gültige Definitionen ohne 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
{- ... -}
Wenn ich also einen Wert val :: ReaderT Int IO Doublehätte, was wäre das Ergebnis von ask. Wir müssten eine Typensignatur wie unten angeben
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
Abgesehen davon, dass es sinnlos ist, ist es nicht überzeugend, den Typ immer wieder zu spezifizieren.
Als Schlussfolgerung unter Verwendung der tatsächlichen Definition von ReaderT. Wenn Sie so etwas wie haben val :: ReaderT String IO Intdie funktionale Abhängigkeit sagt solche Art könnte nur eine einzige Instanz haben MonadReadertypeclass , die derjenige, der Verwendungen sein definiert ist Stringalsr