我发现MonadReader
的{{1}}实例很难理解。
irc的某人提到了一个用例,用于扩展其他人的软件包中的某些多态功能。我不记得他到底是什么意思。这是一个与他所说的话有关的例子,但我不明白这一点。任何人都可以针对(->) r
MonadReader
用例给出另一个示例
(->) r
答案 0 :(得分:2)
重点是使合并所有采用相同环境的功能更加容易。
考虑类型a -> Env -> b
,其中Env
是包含所有“全局”变量的某种数据类型。假设您要组合两个这样的函数。您不能只写h = f2 . f1
,因为f1
的返回类型Env -> b
与f2
的参数类型b
不匹配。
f1 :: a -> Env -> b -- a -> ((->) Env b)
f2 :: b -> Env -> c -- b -> ((->) Env c)
h :: a -> Env -> c
h x e = let v = f1 x e
in f2 v e
由于单子MonadReader
有一个适用的(->) Env
实例,您可以将其写为
-- The class, ignoring default method implementations, is
-- class Monad m => MonadReader r m | m -> r where
-- ask :: m r
-- local :: (r -> r) -> m a -> m a
-- reader :: (r -> a) -> m a
--
-- The functional dependency means that if you try to use (->) Env
-- as the monad, Env is forced to be the type bound to r.
--
-- instance MonadReader r ((->) r) where
-- ask = id
-- local f m = m . f
-- reader = id
h :: MonadReader Env m => a -> m c
h x = do
v <- f1 x
f2 v
-- h x = f1 x >>= f2
没有明确引用环境,h
没有
关心;只有f1
和f2
这样。
更简单地说,您可以使用Kleisli合成运算符定义相同的功能。
import Control.Monad
h :: MonadReader Env m => a -> m c
h = f1 >=> f2
在您的示例中,ask
只是从函数主体内部访问环境的方式,而不是将其作为函数的预先存在的参数。没有MonadReader
实例,您将编写类似
func :: Show a => Bool -> Int -> a -- m ~ (->) Int
func b i = case b of
True -> show i
False -> error "nope"
main
的定义保持不变。但是,(->) Int
不是具有MonadReader
实例的 only 类型;可能会有更复杂的monad堆栈
您在其他地方使用的类型,更通用的类型(Show a, MonadReader Int m) => Bool -> m a
允许您使用它,而不是“只是” (->) Int
。
答案 1 :(得分:1)
我不确定是否打算将用例与Reader
单例分开。
这是一些历史...
transformers
库的灵感来自于一组Functional Programming with Overloading and Higher-Order Polymorphism的讲义(Mark P. Jones,1995年)。在这些注释中,讨论了几个命名的单子(State
,Id
,List
,Maybe
,Error
和Writer
)。例如,Writer
monad类型及其实例定义为:
data Writer a = Result String a
instance Monad Writer where
result x = Result "" x
Result s x ‘bind‘ f = Result (s ++ s’) y
where Result s’ y = f x
还讨论了读者monad,但没有将其定义为单独的类型。而是将Read
类型的别名与直接根据部分应用的函数类型Monad
定义的(->) r
实例一起使用:
type Read r = (r ->)
instance Monad (r->) where
result x = \r -> x
x ‘bind‘ f = \r -> f (x r) r
我实际上不知道这些类型级别的“节” (r ->)
当时是否是有效的Haskell语法。无论如何,这在现代GHC版本中不是有效的语法,但这就是注释中出现的方式。
安迪·吉尔(Andy Gill)创作的transformers
库的第一个版本-或至少是我能够找到的第一个版本,当时它实际上仍是base
库的一部分- -在2001年6月被检入Git。它引入了MonadReader
类和包装了Reader
的新类型:
newtype Reader r a = Reader { runReader :: r -> a }
连同其Functor
,Monad
,MonadFix
和MonadReader
实例。 (没有Applicative
-尚未发明。)它还包含 (->) r
的一组实例,并带有注释:
部分应用的函数类型是一个简单的读取器monad
因此,我认为讲义中的原始表述使安迪(Andy)包括了(->) r
的这些实例,即使他也引入了专用的Reader
新类型以保持与transformers
库中的其他monad。
无论如何,这就是历史。对于用例,我只能想到一个严肃的用例,尽管它可能没有那么引人注目。 lens
库旨在与MonadState
和MonadReader
良好地接口以访问复杂的状态/上下文。因为功能如下:
view :: MonadReader s m => Getting a s a -> m a
preview :: MonadReader s m => Getting (First a) s a -> m (Maybe a)
review :: MonadReader b m => AReview t b -> m t
是根据MonadReader
实例定义的,它们都可以在传统的Reader
上下文中使用:
do ...
outdir <- view (config.directories.output)
...
并在普通函数上下文中:
map (view (links.parent.left)) treeStructure
同样,不一定是引人入胜的用例,但这是一个用例。