我已经看到Reader
在野外多次被用于主要的好处。 (一个值得注意的例子是stack
,built around是Reader
的直接派生词,它可以告诉用户其内容在类型级别上的充分性。)经过一番思考,我得出一个理解,这个好处仅仅是在代码级别上结构,从某种意义上说,Reader
所做的就是在许多地方为复杂的功能线提供参数。也就是说,我开始相信我们总能用一个形状为x
的lambda抽象替换一个拥有λx. ... x ... x ...
的读者。这似乎与声称的official explanations一致:
...部分应用的函数类型( - >)r是一个简单的读者monad ...
但是,要注意Reader
是一种分段写下lambda抽象的方法,声称它是一个部分应用的函数,还有很长的路要走。
λ :t id 1
id 1 :: Num a => a
λ :t 1
1 :: Num a => a
λ :t fromMaybe
fromMaybe :: a -> Maybe a -> a
λ :t flip maybe id
flip maybe id :: b -> Maybe b -> b
甚至忽略了这个作为挑剔,我不会相信(->) r
(为什么不只是写(r ->)
?)是完全和奇怪的Reader
monad。也许我可以编写一个类似于typechecks的实例。也许它甚至会遵守法律。只要我没有把我的功能看作Reader
s,只要我没有正确的直觉,它的视觉,它对我来说就像四色定理的第一个证明一样有用。另一方面,我如何确定这是在函数上定义monad的唯一方法?在Monoid
上有几个Num
s,在Applicative
s上至少有两个List
s - 考虑一个单独的Reader
monad函数是不是太鲁莽?
困境并未在这里结束。一旦我去寻找答案,我偶然发现了一个even more puzzling音符:Reader
恰好是hom
仿函数,或者甚至是一个可表示的仿函数。来自频谱另一端的人们实际上提前知道在Haskell中会有这样的构造,甚至拼写它的类型,就像在上述官方解释中拼写的那样。现在,这是我的想法,但我可以解析来自Mac Lane的hom
仿函数的定义。通过一些想象,可以看出,授予一个函数a -> b
作为(假设)类别Hask中的态射,我们可以用id
组成它以再次获得...函数a -> b
,这次作为集合hom(a, b)
的元素。
这是否与部分应用的某些态射以任何方式连接?或者在Reader
中使用stack
作为选项存储?我真的可以显示hom仿函数Hask的对象和箭头函数 - > Set? (我将把一个endofunctor Hask - > Hask作为一个合理的近似值。)那会是我可靠的伙伴pure
和fmap
吗?
而且,那之后我怎么实际使用Reader
呢?
我不能回答你所有的问题,但让我们从简单的问题开始:
(->) r
(为什么不写(r ->)
?)
因为后者是Haskell中的语法错误。您不能在类型上使用此部分语法。
......声称它是部分应用的功能。
这不是它的意思。引用是:
部分应用的函数类型
(->) r
是一个简单的读者monad
它是部分应用的类型,而不是部分应用的函数。或者换句话说,它的解析如下:((部分应用)(函数类型))
Haskell中函数类型的类型构造函数拼写为->
。
完全应用的函数类型看起来像r -> a
(或等效的(->) r a
),其中r
是参数类型,a
是结果类型。
因此(->) r
是->
(函数类型)的部分应用。
如果我们忽略monad变换器和ReaderT
,Reader
的直接定义是:
newtype Reader r a = Reader (r -> a)
runReader :: Reader r a -> r -> a
runReader (Reader f) x = f x
-- or rather:
runReader :: Reader r a -> (r -> a)
runReader (Reader f) = f
或等效地:
newtype Reader r a = Reader{ runReader :: r -> a }
也就是说,Reader
只是->
(一种非常薄的包装纸)的新类型,runReader
用于展开。
您甚至可以通过复制->
的实例并删除所有MonadReader
/ Reader
包装/展开来使Reader
成为runReader
的实例。