在Snap 框架中,Snaplets 用于通过基于组件的接口将功能嵌入到其他 Snaplet 中:主 Web 应用程序是一个 Snaplet,它通过经典的“has-a”关系引用其他 Snaplet,子 Snaplets 可以反过来引用其他 Snaplets。
在查看各种 Snaplet 实现时,我看到了用于将 Snaplet 嵌入到父 Snaplet 中的不同模式。具体来说:
一种参考。Snaplet 实现假定存在与父 Snaplet 的特定类型的关系。这是通过使用的参考方法强制执行的(见下文)。
一个简单的参考:
data MySnaplet = MySnaplet { subSnaplet :: Snaplet SubSnaplet }
相对镜头:
data MySnaplet = MySnaplet { _subSnaplet :: Snaplet SubSnaplet } subSnaplet :: Lens MySnaplet SubSnaplet subSnaplet = lens _subSnaplet $ \ a b -> a { _subSnaplet = b }
参考方法。Snaplet 实现通过其接口强制执行访问 Snaplet 数据的特定方式,并且不同的 Snaplet 实现使用不同的方法。Snaplet 假设:
MonadState
每次调用操作 Snaplet 的函数时,数据都会出现。- 数据存在于 a 中
MonadState
并包装在Snaplet
包装器中。 - 有一个像
instance HasSubSnaplet MySnaplet
这样的类+实例,它具有用于获取 Snaplet 数据的功能,MySnaplet
前提MySnaplet
是 aMonadState
在调用该函数时位于 a 中。 - 3.中的函数有类型
MySnaplet -> Snaplet SubSnaplet
。 - 有一个像 3. 中那样的类+实例,它提供了一个
Lens MySnaplet (Snaplet SubSnaplet)
. - 类+实例需要一个
Lens (Snaplet MySnaplet) (Snaplet SubSnaplet)
. - class+instance 假定它
MySnaplet
是应用程序的“顶级 Snaplet”,并且需要绝对镜头/参考,因此MySnaplet
必须b
在MonadSnaplet
.
如我所见,如果 Snaplet 是只读的,则引用类型 1. 有意义,如果 Snaplet 需要更改,则引用类型 2. 有意义。
MySnaplet
此外,当只能有一个而没有更多时,为该方法设置一个类是有意义的SubSnaplet
,并且具有绝对引用对于数据库之类的东西可能是有意义的,因为只有顶级 Snaplet 才能访问凭据和什么不是。但是,作为 Snaplet 编写器做出这种假设可能是错误的,使用相对引用来代替也没有任何缺点。
不过,有一个问题:现有的关于 Hackage 的 Snaplets 不符合我所做的这些假设;上述所有方法似乎都是随机使用的,适用于各种情况。此外,我认为上述其他一些方面没有优势/劣势(例如是否需要Snaplet
包装器)。
对我来说,参考类型 2. 和方法 1、2、5 或 6 中的一种似乎在所有情况下都是最有意义的,而且我认为没有理由为什么一直没有就只使用 eg (2, 1) 达成共识.
所以:
作为 Snaplet 编写者,在编写新的 Snaplet 时应该首选哪种方法(假设它具有通用目的),以及
现有的所有 Snaplets 还没有使用相同的参考方法的原因是什么(即使在核心snap
包中,也使用了大量不同的方法)?