MonadReader 類型類
class Monad m => MonadReader r m | m -> r where
ask :: m r
ask = reader id
local :: (r -> r) -> m a -> m a
reader :: (r -> a) -> m a
reader f = do
r <- ask
return (f r)
instance Monad m => MonadReader r (ReaderT r m) where
ask = ReaderT.ask
local = ReaderT.local
reader = ReaderT.reader
asks :: MonadReader r m => (r -> a) -> m a
asks = reader
-
class Monad m => MonadReader r m | m -> r where
MonadReader 是個類型類,它為 ReaderT, RWST 等具有 Reader 功能的 Monad 定義了通用接口。
所謂 Reader 功能是指第一個參數固定的函數,也就是具有固定環境變量的函數。
MonadReader 包含三個函數:ask, local, reader。
ask 擷取環境變量 r。
local f m 通過調用函數 f 局部性地修改環境變量 r,然後調用 Monad m 中封裝的函數。
reader f 對環境變量 r 調用指定函數 f。
另外同一個子產品中還定義了 asks 函數,它與 reader 函數同義。
What’s the “|” for in a Haskell class definition?
-
instance Monad m => MonadReader r (ReaderT r m) where
ask = ReaderT.ask
對于 ReaderT 這個Monad轉換器來說,ask等函數的定義均由 ReaderT 子產品來提供。注意這裡點運算符的含義不是函數的合成而是受限名字。
Hackage - Where is the MonadReader implementation for ReaderT defined?
ReaderT Monad轉換器
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
instance (Monad m) => Monad (ReaderT r m) where
return = lift . return
m >>= k = ReaderT $ \ r -> do
a <- runReaderT m r
runReaderT (k a) r
instance MonadTrans (ReaderT r) where
lift = liftReaderT
liftReaderT :: m a -> ReaderT r m a
liftReaderT m = ReaderT (const m)
-
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
ReaderT 類型是個 newtype,也就是對現有類型的封裝。該類型有三個類型參數:内部 Monad 類型參數 m,共享環境類型參數 r 以及結果類型參數 a。
ReaderT r m a 類型封裝了一個共享環境計算函數:\r -> m a,通過 runReaderT 字段可以從 ReaderT 類型中取出這個函數。
-
instance (Monad m) => Monad (ReaderT r m) where
如果 m 是個 Monad,那麼 ReaderT r m 也是一個 Monad。
對比 Monad 類型類的定義,可知 return 函數的類型簽名為:
return :: a -> ReaderT r m a
大緻相當于 a -> r -> m a
而 bind 函數的類型簽名為:
(>>=) :: ReaderT r m a -> (a -> ReaderT r m b) -> ReaderT r m b
大緻相當于 (r -> m a) -> (a -> r -> m b) -> (r -> m b)
-
return = lift . return
return 函數首先将類型為 a 的值封裝進内部 Monad m 中,然後通過 lift 函數将它封裝進 ReaderT 這個Monad 轉換器之中。
這裡左側的 return 是 ReaderT 這個 Monad 的 return,而右側的 return 是内部 Monad m 的 return。
-
lift = liftReaderT
liftReaderT m = ReaderT (const m)
lift 函數的定義由 liftReaderT 函數提供。
liftReaderT 函數首先将封裝在内部 Monad m 中的值封裝進常值函數 const 中,然後再将它封裝進 ReaderT 這個Monad 轉換器之中。
return a
= lift . return a=liftReaderT.return a
= ReaderT (const (m a))
= ReaderT $ _ -> m a
-
m >>= k = ReaderT $ \r -> do
對比函數簽名,可知 m 的類型是 ReaderT r m a
而 k 的類型是 a -> Reader r m b
-
a <- runReaderT m r
runReaderT m 讓 m 脫離了 ReaderT 這個 Monad,而 <- 運算符讓 runReaderT m r 脫離了内部 Monad m。
-
runReaderT (k a) r
k a 的類型是 ReaderT r m b
runReaderT (k a) 讓 k a 脫離了 ReaderT 這個 Monad,重新進入内部 Monad m。
證明 ReaderT r m 符合Monad法則:
return a >>= f ≡ f a
return a >>= f
≡ (ReaderT $ \_ -> m a) >>= f
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \_ -> m a) r; runReaderT (f a) r}
≡ ReaderT $ \r -> do {a <- (\_ -> m a) r; runReaderT (f a) r}
≡ ReaderT $ \r -> runReaderT (f a) r
≡ ReaderT $ runReaderT (f a)
≡ f a
m >>= return ≡ m
m = ReaderT $ \r -> n a
m >>= return
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (return a) r}
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \r -> n a) r; runReaderT (ReaderT $ \_ -> n a) r}
≡ ReaderT $ \r -> do {a <- (\r -> n a) r; (\_ -> n a) r}
≡ ReaderT $ \r -> n a
≡ m
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
(m >>= f) >>= g
≡ (ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a) r}) >> g
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a) r}) r; runReaderT (g a) r}
≡ ReaderT $ \r -> do {a <- (\r -> do {a <- runReaderT m r; runReaderT (f a) r}) r; runReaderT (g a) r}
≡ ReaderT $ \r -> do {a <- do {a <- runReaderT m r; runReaderT (f a) r}; runReaderT (g a) r}
≡ ReaderT $ \r -> (runReaderT m r >>= \a -> runReaderT (f a) r}) >>= \a -> runReaderT (g a) r
m >>= (\x -> f x >>= g)
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT ((\x -> f x >>= g) a) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a >>= g) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (ReaderT $ \r -> do {a <- runReaderT (f a) r; runReaderT (g a) r}) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; (\r -> do {a <- runReaderT (f a) r; runReaderT (g a) r}) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; do {a <- runReaderT (f a) r; runReaderT (g a) r}}
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> runReaderT (f a) r >>= \a -> runReaderT (g a) r)
根據内部 Monad 的法則:(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
ReaderT $ \r -> (runReaderT m r >>= \a -> runReaderT (f a) r}) >>= \a -> runReaderT (g a) r
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> (\a -> runReaderT (f a) r}) a >>= \a -> runReaderT (g a) r)
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> runReaderT (f a) r >>= \a -> runReaderT (g a) r)
證明 ReaderT 中 lift 函數的定義符合 lift 的法則。
lift . return ≡ return
lift . return $ a
≡ ReaderT (const (return a))
≡ ReaderT (const (m a))
≡ ReaderT $ \_ -> m a
≡ return a
lift (m >>= f) ≡ lift m >>= (lift . f)
假設 m = n a 并且 f a = n b
于是 m >>= f = n b
lift (m >>= f)
≡ ReaderT (const (n b))
≡ ReaderT $ \_ -> n b
lift m >>= (lift . f)
≡ ReaderT (const (n a)) >>= (ReaderT . const . f)
≡ (ReaderT $ \_ -> n a) >>= (\x -> ReaderT . const . f $ x)
≡ ReaderT $ \_ -> runReaderT (ReaderT . const . f $ a) _
≡ ReaderT $ \_ -> runReaderT (ReaderT (const (n b)) _
≡ ReaderT $ \_ -> runReaderT (ReaderT (\_ -> n b) _
≡ ReaderT $ \_ -> n b
ReaderT Monad轉換器的函數
ask :: (Monad m) => ReaderT r m r
ask = ReaderT return
local :: (Monad m) => (r -> r) -> ReaderT r m a -> ReaderT r m a
local = withReaderT
reader :: (Monad m) => (r -> a) -> ReaderT r m a
reader f = ReaderT (return . f)
asks :: (Monad m) => (r -> a) -> ReaderT r m a
asks f = ReaderT (return . f)
mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT f m = ReaderT $ f . runReaderT m
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT f m = ReaderT $ runReaderT m . f
-
withReaderT :: (r’ -> r) -> ReaderT r m a -> ReaderT r’ m a
withReaderT f m = ReaderT $ runReaderT m . f
withReaderT f m 通過調用函數 f 局部性地修改環境變量 r,然後調用 Monad m 中封裝的函數。
-
local :: (Monad m) => (r -> r) -> ReaderT r m a -> ReaderT r m a
local = withReaderT
withReaderT 與 local 的差別在于 withReaderT 能夠改變環境變量 r 的類型。
local 不能改變環境變量 r 的類型,它可以被看做 withReaderT 的特例。
Prelude Control.Monad.Reader> runReaderT ask "abc"
"abc"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (withReaderT length ask) "abc"
Prelude Control.Monad.Reader> runReaderT (mapReaderT (++ ["def"]) ask) "abc"
["abc","def"]
Prelude Control.Monad.Reader> runReaderT (asks (++ "def")) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ask >> ask) "abc"
"abc"
Prelude Control.Monad.Reader> let ioTask = do {v <- ask; liftIO $ print v}
Prelude Control.Monad.Reader> :t ioTask
ioTask :: (MonadReader a m, MonadIO m, Show a) => m ()
Prelude Control.Monad.Reader> runReaderT ioTask "abc"
"abc"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ioTask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ioTask >> ioTask) "abc"
"abcdef"
"abc"
Reader Monad
type Reader r = ReaderT r Identity
runReader :: Reader r a -> r -> a
runReader m = runIdentity . runReaderT m
mapReader :: (a -> b) -> Reader r a -> Reader r b
mapReader f = mapReaderT (Identity . f . runIdentity)
withReader :: (r' -> r) -> Reader r a -> Reader r' a
withReader = withReaderT
Reader Monad 是 ReaderT Monad(轉換器) 的一個特例。
Prelude Control.Monad.Reader> runReader (mapReader (++"def") ask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReader (withReader (++"def") ask) "abc"
"abcdef"
應用執行個體
import Control.Monad.Reader
hello :: Reader String String
hello = do
name <- ask
return ("hello, " ++ name ++ "!")
bye :: Reader String String
bye = do
name <- ask
return ("bye, " ++ name ++ "!")
convo :: Reader String String
convo = do
c1 <- hello
c2 <- bye
return $ c1 ++ c2
main = print . runReader convo $ "adit"
import Control.Monad.Reader
hello :: Reader String String
hello = asks $ \name -> ("hello, " ++ name ++ "!")
bye :: Reader String String
bye = asks $ \name -> ("bye, " ++ name ++ "!")
convo :: Reader String String
convo = asks (const (++)) <*> hello <*> bye
main = print . runReader convo $ "adit"
Reader monad example