为什么有些类型类的前缀是 "Monad"?
Why are some typeclasses prefixed with "Monad"?
阅读一些代码时,我有时会遇到以 Monad
为前缀的类型类,例如 MonadState
、MonadIO
、MonadReader
等
这些的具体用途是什么?
以MonadState
为例,我知道
State
允许无状态状态
StateT
允许使用其他 monad,例如 IO
到 "combine" 两者的功能
但是 MonadState
究竟允许什么?
我不明白是否需要另一组同名的 types/typeclasses,有人可以解释一下吗?
MonadState
不是 monad,而是 Monad
的子类。 State
是一个单子(也是一个 MonadState
)。 monad 子类 MonadState
用于使 get
、put
等函数在具有 gettable/settable "state".[=17 概念的任何 monad 上工作=]
State
、StateT
和其他类似的类型来自transformers
, while the MonadState
typeclass and other typeclasses like it come from mtl
。注意前者是 types,而后者是 typeclasses.
transformers
中的类型是 Monad
和 MonadTrans
的实例。您可以直接与他们合作,但有一些烦恼:
如果你有一个 monad 堆栈深几层,你必须在代码中大量调用 lift
才能访问每一层的功能。
有时两种不同的类型提供相同的 "interface"。例如,RWST
和 ReaderT
都提供类似 reader 的功能,例如 ask
。在编写一个函数时,不得不提交另一个函数是很烦人的,因为它降低了通用性。
来自 mtl
的 Monad*
类型类缓解了这些问题:
他们有 "pass-through" 个实例,可以消除对 lift
的许多调用(或者更准确地说,自动处理它们)。例如,StateT
是 MonadState
的实例,但是 StateT
上的 ReaderT
也是 MonadState
的实例,因此您可以使用 get
直接。
import Control.Monad
import Control.Monad.Reader
import Control.Monad.State
-- put the environment in the state
bar :: ReaderT Int (State Int) ()
bar = ask >>= lift . put -- we use lift here
barMTL :: ReaderT Int (State Int) ()
-- This ONLY works if we have imported the
-- required instances from mtl.
-- The MonadState instance for ReaderT, in particular.
barMTL = ask >>= put -- the put is auto-lifted
您可以在您的函数中放置一个 Monad*
约束并针对 Monad*
"interface" 而不是立即提交 monad 的特定实现。这样你的函数就会变得更通用,并且选择确切的 "implementation" 会延迟到最后可能的时刻。
import Control.Monad
import Control.Monad.State
import Control.Monad.RWS
-- Dumb function that increments the state.
-- Doesn't commit to a specific implementation of the monad.
baz :: MonadState Int m => m ()
baz = modify succ
main :: IO ()
main = do
-- run as State
print $ runState baz 0
-- run as RWS
print $ runRWS (baz >> tell ()) () 0
阅读一些代码时,我有时会遇到以 Monad
为前缀的类型类,例如 MonadState
、MonadIO
、MonadReader
等
这些的具体用途是什么?
以MonadState
为例,我知道
State
允许无状态状态StateT
允许使用其他 monad,例如IO
到 "combine" 两者的功能
但是 MonadState
究竟允许什么?
我不明白是否需要另一组同名的 types/typeclasses,有人可以解释一下吗?
MonadState
不是 monad,而是 Monad
的子类。 State
是一个单子(也是一个 MonadState
)。 monad 子类 MonadState
用于使 get
、put
等函数在具有 gettable/settable "state".[=17 概念的任何 monad 上工作=]
State
、StateT
和其他类似的类型来自transformers
, while the MonadState
typeclass and other typeclasses like it come from mtl
。注意前者是 types,而后者是 typeclasses.
transformers
中的类型是 Monad
和 MonadTrans
的实例。您可以直接与他们合作,但有一些烦恼:
如果你有一个 monad 堆栈深几层,你必须在代码中大量调用
lift
才能访问每一层的功能。有时两种不同的类型提供相同的 "interface"。例如,
RWST
和ReaderT
都提供类似 reader 的功能,例如ask
。在编写一个函数时,不得不提交另一个函数是很烦人的,因为它降低了通用性。
来自 mtl
的 Monad*
类型类缓解了这些问题:
他们有 "pass-through" 个实例,可以消除对
lift
的许多调用(或者更准确地说,自动处理它们)。例如,StateT
是MonadState
的实例,但是StateT
上的ReaderT
也是MonadState
的实例,因此您可以使用get
直接。import Control.Monad import Control.Monad.Reader import Control.Monad.State -- put the environment in the state bar :: ReaderT Int (State Int) () bar = ask >>= lift . put -- we use lift here barMTL :: ReaderT Int (State Int) () -- This ONLY works if we have imported the -- required instances from mtl. -- The MonadState instance for ReaderT, in particular. barMTL = ask >>= put -- the put is auto-lifted
您可以在您的函数中放置一个
Monad*
约束并针对Monad*
"interface" 而不是立即提交 monad 的特定实现。这样你的函数就会变得更通用,并且选择确切的 "implementation" 会延迟到最后可能的时刻。import Control.Monad import Control.Monad.State import Control.Monad.RWS -- Dumb function that increments the state. -- Doesn't commit to a specific implementation of the monad. baz :: MonadState Int m => m () baz = modify succ main :: IO () main = do -- run as State print $ runState baz 0 -- run as RWS print $ runRWS (baz >> tell ()) () 0