Klasa MonadReader
określona jest rozszerzenie FunctionalDependencies
, co pozwala zgłoszeń jak
class Monad m => MonadReader r m | m -> r where
...
Oznacza to, że dla każdego monadzie m
The r
jest jednoznacznie określony przez nią. Dlatego nie możesz mieć pojedynczej monady m
, która określa dwa różne typy r
. Bez tego, jako ograniczenia, kompilator nie będzie w stanie wpisać sprawdzających zastosowań klasy.
Rozwiązaniem tego problemu jest, aby napisać swoje funkcje jak
getA'sInt :: A -> Int
getA'sInt = undefined
getB'sString :: B -> String
getB'sString = undefined
foo :: (MonadReader A m) => m Int
foo = do
a <- asks getA'sInt
return $ a + 1
bar :: (MonadReader B m) => m String
bar = do
b <- asks getB'sString
return $ map toUpper b
Następnie wystarczy użyć krotki (A, B)
w swojej rzeczywistej realizacji:
baz :: Reader (A, B) (Int, String)
baz = do
a <- withReader fst foo
b <- withReader snd bar
return (a, b)
Jest też withReaderT
dla bardziej skomplikowanych przypadków.
Jako przykład, dlaczego nie wolno układać ReaderT
s, należy rozważyć przypadek
type App = ReaderT Int (Reader Int)
Po wywołaniu ask
, który Int
pan myśli? To może wydawać się oczywiste, że w przypadkach takich jak
type App = ReaderT A (Reader B)
kompilator powinien być w stanie dowiedzieć się, które w użyciu, ale problemem jest to, że ask
funkcja tutaj musiałby typ
ask :: App ???
Gdzie ???
mogli być A
lub B
. Można obejść ten inny sposób, nie używając MonadReader
bezpośrednio i definiowanie konkretnych askA
i askB
funkcje:
type App = ReaderT A (Reader B)
askA :: App A
askA = ask
askB :: App B
askB = lift ask
baz :: App (Int, String)
baz = do
a <- askA
b <- askB
return (getA'sInt a, getB'sString b)
Ale będziesz tylko mógł mieć MonadReader A App
, nie można też mieć MonadReader B App
. Takie podejście można nazwać "wyraźnym podnoszeniem", a to sprawia, że funkcje te są specyficzne dla typu App
, a zatem są mniej kompozycyjne.
Oh, 'instancja (MonadReader r m) => MonadReader r (ReaderT s m)" nawet nie istnieje ?! Dlaczego? –