Haskell 为什么没有`-XDeriveApplicative`扩展?
GHC有几种有用的语言可以机械地派生各种常见的Haskell类型类(Haskell 为什么没有`-XDeriveApplicative`扩展?,haskell,ghc,Haskell,Ghc,GHC有几种有用的语言可以机械地派生各种常见的Haskell类型类(-XDeriveFunctor,-XDeriveFoldable,-XDeriveTraversable)。似乎,Applicative是另一个经常需要并且经常容易派生的类。对于包含a类型插槽的简单记录,例如 data SimpleRecord a=简单a Applicative实例的派生非常简单 实例应用程序简单记录,其中 纯x=简单x 简单a1 b1 c1简单a2 b2 c2=简单(a1 a2)(b1 b2)(c1 c2)
-XDeriveFunctor
,-XDeriveFoldable
,-XDeriveTraversable
)。似乎,Applicative
是另一个经常需要并且经常容易派生的类。对于包含a
类型插槽的简单记录,例如
data SimpleRecord a=简单a
Applicative
实例的派生非常简单
实例应用程序简单记录,其中
纯x=简单x
简单a1 b1 c1简单a2 b2 c2=简单(a1 a2)(b1 b2)(c1 c2)
即使在稍微困难一些的情况下,一些a
值隐藏在其他应用函子中,例如
data MyRecord f a=MyRecord(f a)a
一个合理的例子很容易写出来
instance(Applicative f)=>Applicative(MyRecord f)其中
纯x=MyRecord(纯x)x
MyRecord a1 b1 MyRecord a2 b2=MyRecord(a1 a2)(b1 b1)
为什么实现这些机械实例的
-XDeriveApplicative
扩展不存在?即使是派生
和通用派生
包显然也缺乏应用性
支持。是否有一个理论问题阻止这些实例通常有效(除了那些可能威胁到函子
、可折叠
、或可遍历
扩展的原因之外)?对于遵循函子定律的给定数据类型,最多有一个函子
实例。例如,map
是列表的fmap
的唯一合法实现:
fmap id == id
fmap (f . g) == fmap f . fmap g
但是,Applicative
的守法实例可能不止一个,这并不一定显而易见
pure id <*> v == v
pure (.) <*> u <*> v <*> w == u <*> (v <*> w)
pure f <*> pure x == pure (f x)
u <*> pure y == pure ($ y) <*> u
pure id v==v
纯(.)u v w==u(v w)
纯f纯x==纯(f x)
u纯y==纯($y)u
对于列表,
可以表现得像\fs xs->concatMap(\f->map f xs)fs
或者像zipWith($)
,编译器应该选择哪一个并不清楚。为了回应其他人,我不知道为什么我们不能有-XDeriveApplicative
,我们只是碰巧没有。通常有多个合法的可折叠
和可遍历
实例,我们有一个标志来派生这些实例。有一段时间,我们没有关于可穿越定律的真正好故事,但现在。类似地,我们仍然没有可折叠的法则(但我认为我们可以,见)
在不同的“明显的”应用程序中,例如向前和向后的应用程序(相对于
本身,甚至相对于fa
中的多个a
进行排列,如果存在这样的应用程序),那么按照这里的建议构建应用程序,并按照语法顺序进行遍历,似乎是合法的。然而,对于递归类型,例如列表,甚至是具有多个构造函数的类型,我们选择的有效应用程序以有趣的方式蓬勃发展。对于类似列表的正则递归类型,明显的应用选择自然是“zipLike”一次,因为它以自然的方式概括了非递归的情况,使结构与结构相匹配。对于具有多个构造函数的求和类型,“显而易见”的选择更难定义
在我看来,applicative的一个完全合理的派生将如本文所建议的那样工作,在只有一个构造函数的类型(包括递归类型)上按语法顺序工作。在多个构造函数的情况下,失败似乎是合法的
另一方面,虽然可折叠和可遍历似乎经常以“明显”的形式出现,但与有趣的应用程序相比,我不太清楚我们希望定义“明显”应用程序的次数。我的直觉告诉我,这个功能很少使用,可能只是不经常有用。在
纯
的情况下,事情似乎很明确(您只有一个a
,并且可以在假定每个组成函子都适用的情况下将其明确地插入结构中。在
的情况下,存在排序问题,但这似乎完全合理(通常是用户的意图)简单地为单个构造函数类型应用相应的字段。对于任何更复杂的(包括ADT),编译器可以简单地拒绝派生实例。同样的反对意见也可以用于-XDeriveTraversable
,不是吗?Traversable
定义规定应该对操作进行排序“从左到右"虽然我仍然觉得这并不总是很清楚这意味着什么。派生Ord的方法也有很多,只要能很好地指定派生的作用,这就不是问题了。@bgamari实际上甚至pure
也不是毫不含糊的。对于列表的标准应用程序函子pure
提供了一个单例列表,但是为了使应用定律发挥作用,ZipList
pure
必须为您提供注入值的无限重复列表!@augustss:一个区别是,在许多情况下,只要使用一致,使用什么顺序都无关紧要。与applicative
相比,后者主要对actu有用实例的所有属性。