Tôi nghĩ nguồn gốc của sự nhầm lẫn là trong định nghĩa của
class Monad m => MonadReader r m | m -> r where
{- ... -}
Nó được ngầm hiểu là m
có chứa r
chính nó (đối với các trường hợp phổ biến). Hãy để tôi sử dụng một definiton nhẹ hơn Reader
như
newtype Reader r a = Reader {runReader :: r -> a}
Khi r
tham số được chọn, bạn có thể dễ dàng xác định một thể hiện đơn nguyên cho Reader r
. Điều đó có nghĩa là trong định nghĩa lớp loại m
nên được thay thế cho Reader r
. Vì vậy, hãy xem làm thế nào expresion kết thúc là:
instance MonadReader r (Reader r) where -- hey!! r is duplicated now
{- ... -} -- The functional dependecy becomes Reader r -> r which makes sense
Nhưng tại sao chúng ta cần điều này? Nhìn vào định nghĩa của ask
trong MonadReader
lớp.
class Monad m => MonadReader r m | m -> r where
ask :: m r -- r and m are polymorphic here
{- ... -}
Không có fun-dep, không có gì có thể ngăn tôi xác định ask
theo cách trả về một loại khác như trạng thái. Thậm chí, tôi có thể định nghĩa nhiều phiên bản của trình đọc đơn âm cho loại của mình. Ví dụ, đây sẽ là định nghĩa hợp lệ mà không cần 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
{- ... -}
Vì vậy, nếu tôi có một giá trị val :: ReaderT Int IO Double
, kết quả của nó là gì ask
. Chúng tôi cần xác định một chữ ký loại như dưới đây
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
Bên cạnh việc vô cảm, thật không thuận tiện khi chỉ định loại này nhiều lần.
Như một kết luận sử dụng định nghĩa thực tế của ReaderT
. Khi bạn có một cái gì đó giống như val :: ReaderT String IO Int
sự phụ thuộc chức năng nói một kiểu như vậy có thể chỉ có một trường hợp duy nhất của MonadReader
typeclass được định nghĩa là một trong đó sử dụng String
nhưr