为什么我们需要Control.Lens.Reified?

时间:2015-10-12 19:14:26

标签: haskell lenses

为什么我们需要Control.Lens.Reified?有什么理由我不能将Lens直接放入容器中吗?无论如何reify意味着什么?

2 个答案:

答案 0 :(得分:12)

我们需要具体化的镜头,因为Haskell的类型系统具有预测性。我不知道究竟是什么意思的技术细节,但它禁止像

这样的类型
[Lens s t a b]

出于某些目的,使用

是可以接受的
Functor f => [(a -> f b) -> s -> f t]
相反,但是当你达到这个目标时,你就不会得到Lens;你得到LensLike专门用于某个仿函数或其他仿函数。 ReifiedBlah newtypes让你可以继续使用完整的多态性。

在操作上,[ReifiedLens s t a b]是一个函数列表,每个函数都带有Functor f字典,而forall f . Functor f => [LensLike f s t a b]是一个带有Functor f字典并返回列表的函数。

至于什么" reify"意思是,字典会说些什么,而这似乎在Haskell中转化为一种相当令人惊叹的特定含义。所以没有评论。

答案 1 :(得分:7)

问题在于,在Haskell中,类型抽象和应用程序是完全隐含的;编译器应该在需要的地方插入它们。设计“impredicative”扩展的各种尝试都失败了,编译器会巧妙地猜测在哪里放置它们。所以最安全的事情最终依赖于Haskell 98规则:

  • 类型抽象仅出现在函数定义的顶层。
  • 只要在表达式中使用具有多态类型的变量,就会立即发生类型应用程序。

所以,如果我定义一个简单的镜头:[1]

lensHead f [] = pure []
lensHead f (x:xn) = (:xn) <$> f x

并在表达式中使用它:

[lensHead]

lensHead自动应用于某些类型参数集;在这一点上,它不再是一个镜头,因为它在仿函数中不再是多态的。外卖是:表达式总是具有一些单形类型;所以它不是镜头。 (您会注意到lens函数采用类型为GetterSetter的参数,这些参数是单态类型,原因与此类似。但是[Getter s a]不是一系列镜头,因为它们专门用于吸气剂。)

reify是什么意思?字典定义是'make real'。哲学中使用“具体化”来指代将事物视为真实(而不是理想或抽象)的行为。在编程中,它倾向于指采取通常不能被视为数据结构并将其表示为一个的东西。例如,在非常古老的Lisps中,没有使用成为一流的函数;相反,你必须使用S-Expressions传递“函数”,并在需要调用函数时使用eval它们。 S-Expressions以您可以在程序中操作的方式表示函数,这被称为 reification

在Haskell中,我们通常不需要像Lisp S-Expressions那样精心设计的具体策略,部分原因是因为语言旨在避免需要它们;但是从那以后

newtype ReifiedLens s t a b = ReifiedLens (Lens s t a b)

与获取多态值并将其转换为真正的第一类值具有相同的效果,它被称为具体化。

如果表达式总是具有单态类型,为什么会这样?好吧,因为Rank2Types扩展添加了第三条规则:

  • 类型抽象出现在某些函数的参数的顶层,即所谓的排名2类型。

ReifiedLens是一个排名-2功能;所以当你说

ReifiedLens l

ReifiedLens的参数周围得到一个类型lambda,然后l立即应用于lambda-bound类型参数。所以l实际上只是eta扩展。 (编译器可以自由地减少这种情况,直接使用l)。

然后,当你说

f (ReifiedLens l) = ...

在右侧,l是一个具有多态类型的变量,因此每次使用l都会立即隐式分配给表达式进行类型检查所需的任何类型参数。所以一切都按照你期望的方式运作。

另一种思考方式是,如果你说

newtype ReifiedLens s t a b = ReifiedLens { unReify :: Lens s t a b }

两个函数ReifiedLensunReify就像显式类型抽象和应用程序运算符;这允许编译器确定您希望抽象和应用程序在哪里发生,以至于不会出现带有impredicative类型系统的问题。

[1]在lens术语中,这显然被称为“镜头”以外的东西;我对镜头的全部了解都来自SPJ对它们的介绍,所以我无法对其进行验证。重点仍然是,因为多态性仍然是必要的,以使其作为吸气剂和设定者。