在Snap Framework中,Snaplet用于通过基于组件的界面将功能嵌入到其他Snaplet中:主Web应用程序是一个Snaplet,通过经典的“has-a”关系引用其他Snaplet,以及sub -Snaplets可以反过来引用其他Snaplet。
在查看各种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
中。MonadState
中,并包含在Snaplet
包装中。instance HasSubSnaplet MySnaplet
的类+实例,它具有从MySnaplet
中获取Snaplet数据的功能,前提是此时MySnaplet
位于MonadState
调用函数。MySnaplet -> Snaplet SubSnaplet
。Lens MySnaplet (Snaplet SubSnaplet)
。Lens (Snaplet MySnaplet) (Snaplet SubSnaplet)
。MySnaplet
是应用程序的“顶级Snaplet”,并且需要绝对镜头/引用,因此MySnaplet
必须是b
中的MonadSnaplet
{1}}。正如我所看到的,如果Snaplet是只读的,那么引用类型1是有意义的。如果Snaplet需要更改,则有意义。
此外,当MySnaplet
只能有一个SubSnaplet
而没有更多时,有一个方法的类是有意义的,并且具有绝对引用可能对数据库之类的东西有意义,这些不可能被配置作为一个组件,假设只有顶级Snaplet可以访问凭据而不是。但是,将此假设作为Snaplet编写器可能是错误的,并且使用相对引用不会有任何缺点。
但是有一个proglem:Hackage上的现有Snaplet不符合我所做的这些假设;上述所有方法似乎都是随机和在各种情况下使用的。此外,我认为上述某些其他方面没有优势/劣势(例如需要Snaplet
包装器)。
对我来说,参考类型2.方法1,2,5或6中的一个似乎在所有情况下最有意义,并且我认为没有理由仅为什么没有达成共识使用例如(2,1)一直。
所以:
作为Snaplet编写者,在编写新的Snaplet时应该首选哪种方法(假设它具有通用性),
为什么现有的所有Snaplet都没有使用相同的参考方法(即使在核心snap
包中,也会使用大量不同的方法)? < / p>
答案 0 :(得分:1)
TLDR;大多数情况下,您可能希望使用with
功能和相对镜头。
使用HasSubSnaplet类型类是一个完全可选的模式,在没有多个SubSnaplet实例的情况下,可以减少“with subSnaplet”样板。我们选择为snap包中的Heist snaplet执行此操作,因为它对Heist snaplet有意义,并为用户提供模式的示例。
由于类型类是完全可选的并且与镜头的选择大致正交,因此对于本答案的其余部分,我将专注于没有类型类的操作。
API的目的是使用镜头(而不是“简单引用”来获取Snaplet数据类型中的内容)来访问您的状态。这是因为在请求处理过程中改变状态是我们希望Snaplet提供的基本能力。在大多数情况下,我们打算让Snaplet
成为一个不透明的包装器,最终用户除了snaplet的状态类型之外不需要触摸它。这就是MonadState实例在没有Snaplet包装器的情况下直接将您带到您的类型的原因。
据说,有四种基本的访问模式。我们可以看到这些MonadSnaplet type class。
with :: Lens v (Snaplet v') -> m b v' a -> m b v a
withTop :: Lens b (Snaplet v') -> m b v' a -> m b v a
with' :: Lens (Snaplet v) (Snaplet v') -> m b v' a -> m b v a
withTop' :: Lens (Snaplet b) (Snaplet v') -> m b v' a -> m b v a
前两个功能中体现的镜头模式是由带有TemplateHaskell的数据镜头模板包自动生成的镜头类型,是最自然的。因此,这是推荐的模式。 (因此,较短的,未引用的名称。)with
和withTop
之间的区别在于with
采用相对镜头而withTop
采用绝对镜头。大多数时候我使用相对镜头。但我们想允许使用绝对镜头,因为我可以想象复杂的应用程序,其中一个snaplet可能需要获取另一个snaplet提供的东西,但不是当前snaplet的后代。
偶尔会出现您希望能够拥有身份镜头的情况。这需要Lens (Snaplet a) (Snaplet b)
。因此,除了采用这种镜头之外,第二个两个引导功能类似于前两个功能。