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
扩展的原因?
答案 0 :(得分:13)
对于遵循仿函数法则的给定数据类型,最多只有一个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 ($)
,并且不清楚编译器应该选择哪一个。
答案 1 :(得分:6)
为了回应别人,没有充分的理由我知道为什么我们不能-XDeriveApplicative
,我们根本不会这样做。 Foldable
和Traversable
通常有多个合法的实例,我们有一个标志来推导它们。有一段时间我们没有关于可穿越法律的真实好故事,但现在we have some。同样,我们仍然没有Foldable
法律(但我认为可以,请参阅here)。
在不同的&#39;明显的&#39;应用程序,例如向前和向后的应用程序(与<*>
本身相对应,甚至在a
f a
中对{{1}}进行置换,然后根据建议构建应用程序在这里,以句法顺序遍历,似乎是合法的。但是,对于诸如列表之类的递归类型,甚至是具有多个构造函数的类型,我们选择有效的应用程序都会以有趣的方式开花。对于类似列表的常规递归类型,明显的应用选择自然是&#34; zipLike&#34;曾经,因为它以自然的方式推广非递归情况,匹配结构与结构。对于具有多个构造函数的sum类型,&#34;显而易见的&#34;选择更难定义。
在我看来,完全合理的applicative派生将按照这里的建议工作,只有一个构造函数的类型(包括递归的)的语法顺序。在多个构造函数的情况下,失败似乎是合法的。
另一方面,虽然Foldable和Traversable似乎经常出现在他们的'#34;显而易见的&#34;形式,对我来说不太清楚我们希望定义多少次&#34;明显&#34;应用程序,与有趣的相比。我的直觉告诉我,这个特征很少被运用,也许根本不常用。
答案 2 :(得分:0)
GHC 基础的下一个版本将包括
newtype Generically a = Generically a
newtype Generically1 f a = Generically1 (f a)
在GHC.Generics
。
这允许开箱即用地派生这些实例
{-# Language DeriveGeneric #-}
{-# Language DerivingVia #-}
{-# Language DerivingStrategies #-}
..
import GHC.Generics
data SimpleRecord a = Simple a a a
deriving
stock Generic1
deriving (Functor, Applicative)
via Generically1 SimpleRecord
data MyRecord f a = MyRecord (f a) a
deriving
stock Generic1
deriving (Functor, Applicative)
via Generically1 (MyRecord f)