TL;DR - 没有差距。您粘贴的处理程序定义已过期。当前的使用Lensed。
长答案:我们没有任何关于此的文档,因为它是一个低级的实现细节——即所有这些都对最终用户完全隐藏。卡尔是对的,RST 只是RWST减去 W,但让我们做一些更深入的调查。使用您在上面显示的类型,我们将 RST 的定义替换为 LensT 定义。这给了我们以下替换:
r = Lens b v
s = s
m = m
a = a
有了它,我们可以轻松编写扩展的 LensT 定义:
newtype LensT b v s m a = LensT { unlensT :: Lens b v -> s -> m (a, s) }
将其与 Lensed 进行比较:
newtype Lensed b v m a = Lensed { unlensed :: ALens' b v -> v -> b -> m (a, v, b) }
如果我们假设Lens b v
andALens' b v
是可互换的(从概念上讲,它们是可互换的),那么您可以看到这种等价性:
Lensed b v m a = LensT b v (b,v) m a
现在我们看到了问题的症结所在。LensT 是比 Lensed 更通用的构造。LensT 允许您s
任意选择。镜头修复s
完全由b
和决定v
。现在我们了解了区别,问题是这两个结构在 Snap 中是如何实际使用的。快速 grepTypes.hs
向我们展示了 Handler 使用 Lensed 而 Initializer 使用 LensT。(附注:您为 Handler 提供的定义不是我们当前使用的定义。)以下是定义的重要部分。
Handler b v a = Handler (L.Lensed (Snaplet b) (Snaplet v) ...)
Initializer b v a = Initializer (LT.LensT (Snaplet b) (Snaplet v) (InitializerState b)...)
Initializer 使用更通用的 LensT 构造,因为它需要s
be ,其中包含与andInitializerState
无关的额外信息。事实上,Initializer 存在的全部意义在于便于构造将用作 Handler 初始状态的 a。Initializer 在您的应用程序启动时运行,但 Handler 是您的应用程序在其中运行的。我们希望 Handler 尽可能高效,因此我们创建了 Lensed 来针对 Handler 的需求进行优化。您可能会说这是过早的优化,但由于其他人为我做了,我不会拒绝。b
v
b
您可能想知道为什么我们甚至还有 Lensed 和 LensT。它们只用在一个地方,所以我们可以将它们的定义分别替换为 Handler 和 Initializer 。那是因为我们一开始并没有 Lensed。Handler 和 Initializer 都是用 LensT 编写的,因此消除重复代码是一个完全合理的抽象。Lensed 是后来出现的,因为无论如何它们都是新类型,所以这些抽象层强制实现零运行时成本。