Haskell 如何理解Snaplet中RST、Lensed和LensT的提取?

Haskell 如何理解Snaplet中RST、Lensed和LensT的提取?,haskell,haskell-snap-framework,haskell-lens,Haskell,Haskell Snap Framework,Haskell Lens,我最近正在阅读Snap的源代码,这很好,但是当我继续阅读Snaplet处理程序源代码时,我被RST、Lensed和LensT的抽象所困扰 newtype RST r s m a = RST { runRST :: r -> s -> m (a, s) } newtype LensT b v s m a = LensT (RST (Lens b v) s m a) newtype Handler b v a = Handler (LensT (Snaplet b) (Snaplet v

我最近正在阅读Snap的源代码,这很好,但是当我继续阅读Snaplet处理程序源代码时,我被RST、Lensed和LensT的抽象所困扰

newtype RST r s m a = RST { runRST :: r -> s -> m (a, s) }
newtype LensT b v s m a = LensT (RST (Lens b v) s m a)
newtype Handler b v a = Handler (LensT (Snaplet b) (Snaplet v) (Snaplet b) Snap a)
现在伦斯特变成了伦斯特

newtype Lensed b v m a = Lensed { unlensed :: ALens' b v -> v -> b -> m (a, v, b) }
Snaplet设计
我们转向了一种更专业的单子公式,称为Lensed,它避免了在操纵状态时遍历整个状态层次结构。


我觉得Snap和Snaplet处理程序的实现之间存在差距,关键是RST、LensT和Lensed,是否有任何参考文档可以帮助我;博士-没有差距。粘贴的处理程序定义已过期。使用透镜

详细回答:我们没有关于这方面的任何文档,因为这是一个低级的实现细节——也就是说,所有这些都对最终用户完全隐藏。卡尔是对的,RST只是减去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) }
如果我们假设
lensbv
ALens'bv
是可互换的(从概念上讲,它们是可互换的),那么您可以看到这种等价性:

Lensed b v m a = LensT b v (b,v) m a
现在我们看到了问题的症结所在。LensT是比Lensed更一般的构造。LensT允许您任意选择
s
。透镜固定的
s
完全由
b
v
确定。现在,我们了解了差异,问题是这两个构造在Snap中实际如何使用。通过
Types.hs
的快速grep,我们可以看到处理程序使用Lensed,而初始值设定项使用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)...)
初始化器使用更一般的LensT结构,因为它需要
s
成为
InitializerState
,其中包含与
b
v
无关的额外信息。事实上,初始值设定项存在的全部目的是为了促进将用作处理程序初始状态的a
b
的构造。初始值设定项在应用程序启动时运行,但处理程序是应用程序运行的对象。我们希望处理程序尽可能高效,所以我们创建了Lensed,以便针对处理程序的需求进行优化。你可以说这是过早的优化,但既然有人帮我做了,我就不会说不


你可能想知道为什么我们会有透镜和透镜。它们只在一个地方使用,所以我们可以将它们的定义分别替换为Handler和Initializer。那是因为我们一开始没有透镜。处理程序和初始值设定项都是用LensT编写的,因此消除重复代码是完全合理的抽象。Lensed来得晚,因为它们都是新类型,所以这些抽象层的运行时成本为零。

RST
RWST
,没有
Writer
部分,这是为了提高效率而忽略的。看见