GHC有几种有用的语言extensions,用于机械地导出各种常见的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
扩展的原因之外)?
3条答案
按热度按时间ngynwnxp1#
对于给定的数据类型,最多只能有一个
Functor
的示例遵循函子法则。例如,map
是fmap
在列表中的唯一合法实现:但是
Applicative
可能有不止一个守法的示例,这并不一定显而易见。对于列表,
<*>
可以像\fs xs -> concatMap (\f -> map f xs) fs
或zipWith ($)
那样工作,并且不清楚编译器应该选择哪一个。tzcvj98z2#
为了呼应其他人的观点,我不知道为什么我们不能有
-XDeriveApplicative
,我们只是碰巧没有。通常有不止一个Foldable
和Traversable
的合法示例,我们有一个标志来派生这些示例。有一段时间,我们没有关于可遍历定律的真实的好故事,但是现在we have some。同样地,我们仍然没有Foldable
定律(但是我认为我们 * 可以 *,参见here)。在不同的“明显”应用句中,例如向前和向后的应用句(维斯a vis
<*>
本身,甚至vis a visf a
中的多个a
,如果存在这样的情况),那么按照这里的建议构建应用程序,并按语法顺序遍历,似乎是合法的。我们对有效应用的选择以有趣的方式展开。对于列表式的正则递归类型,明显的应用选择自然是“zipLike”一次,因为它以自然的方式推广了非递归情况,使结构与结构匹配。对于具有多个构造函数的求和类型,“明显的”选择要难定义得多。在我看来,一个完全合理的applicative派生应该像这里所建议的那样工作,按照只有一个构造函数的类型(包括递归类型)的语法顺序。在多个构造函数的情况下,失败似乎是合理的。
另一方面,虽然Foldable和Traversable似乎经常以“显而易见”的形式出现,但我不太清楚,与有趣的形式相比,我们有多少次希望定义“显而易见”的应用。我的直觉告诉我,这个特性很少被使用,也许只是不经常有用。
polkgigr3#
下一个GHC基本版本将包括
newtype Generically a = Generically a
newtype Generically1 f a = Generically1 (f a)
个在
GHC.Generics
中。这样就可以直接派生出这些示例
这只适用于乘积类型,求和类型的应用性要多样化得多,因为我们需要一种连贯的方式来协调构造函数之间的关系。这需要一个函数来保持应用性结构(应用性态射),在我写的这个包中称为
Idiom
:https://hackage.haskell.org/package/idiomatic
这个包允许派生具有应用态射的类型级别配置的总和类型的应用。