为什么我们需要Control.Lens.Reified?有什么理由我不能将Lens
直接放入容器中吗?无论如何reify
意味着什么?
答案 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
函数采用类型为Getter
和Setter
的参数,这些参数是单态类型,原因与此类似。但是[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扩展添加了第三条规则:
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 }
两个函数ReifiedLens
和unReify
就像显式类型抽象和应用程序运算符;这允许编译器确定您希望抽象和应用程序在哪里发生,以至于不会出现带有impredicative类型系统的问题。
[1]在lens
术语中,这显然被称为“镜头”以外的东西;我对镜头的全部了解都来自SPJ对它们的介绍,所以我无法对其进行验证。重点仍然是,因为多态性仍然是必要的,以使其作为吸气剂和设定者。