为什么没有`
GHC有几种有用的语言扩展,用于机械派生各种常见的Haskell类型类( -XDeriveFunctor
, -XDeriveFoldable
, -XDeriveTraversable
)。 看起来Applicative
是另一类经常需要且经常容易派生的类。 对于包含类型a
插槽的简单记录,例如,
data SimpleRecord a = Simple a a a
Applicative
实例是平凡派生的,
instance Applicative SimpleRecord where
pure x = Simple x x x
Simple a1 b1 c1 <*> Simple a2 b2 c2 = Simple (a1 a2) (b1 b2) (c1 c2)
即使在一些稍硬的情况下a
值都埋在其他应用性仿函数,例如,
data MyRecord f a = MyRecord (f a) a
一个合理的实例很容易写出来,
instance (Applicative f) => Applicative (MyRecord f) where
pure x = MyRecord (pure x) x
MyRecord a1 b1 <*> MyRecord a2 b2 = MyRecord (a1 <*> a2) (b1 b1)
为什么实现这些机械实例的-XDeriveApplicative
扩展不存在? 即使derive
和generic-derive
软件包显然缺乏Applicative
支持。 是否有理论上的问题阻止这些实例通常是有效的(除了那些也可能威胁Functor
, Foldable
或可Traversable
扩展的原因)?
对于给定的数据类型, Functor
最多只有一个实例遵循函子法则。 例如, 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
对于列表, <*>
行为可能像fs xs -> concatMap (f -> map f xs) fs
或像zipWith ($)
,并且不清楚编译器应该选择哪一个。
为了呼应别人,我没有很好的理由知道为什么我们不能拥有-XDeriveApplicative
,我们只是碰巧不会。 通常有不止一个合法的Foldable
和Traversable
实例,我们有一个标志来推导这些实例。 有一段时间,我们没有真正的可穿越法律的好故事,但现在我们有一些。 同样,我们还没有Foldable
法律(但我认为我们可以,在这里看到)。
之间的不同的“明显的” applicatives,诸如向前和向后酮(相一相<*>
本身,甚至置换相一相的多个a
在fa
,如果有这样的),然后建立applicatives如这里提出,以遍历在句法顺序上,似乎是合法的。 但是,对于列表等递归类型,甚至包含多个构造函数的类型,我们选择的有效应用程序以有趣的方式开花。 对于列表式的常规递归类型,明显的应用选择自然是“zipLike”一次,因为它以自然的方式推广非递归的情况,将结构与结构进行匹配。 对于具有多个构造函数的求和类型,“明显”的选择很难定义。
在我看来,一个完全合理的应用派生将按照这里所建议的方式工作,只需要一个构造函数就可以按类型(包括递归)的语法顺序进行操作。 在多构造函数的情况下,失败似乎是合法的。
另一方面,尽管“可折叠”和“可穿越”似乎经常以其“明显”的形式出现,但对于我而言,与有趣的相比,我们有多少次想要定义“明显”应用。 我的直觉告诉我,这个功能很少会被使用,也许根本不经常使用。
链接地址: http://www.djcxy.com/p/43455.html上一篇: Why is there no `