为什么没有`-XDeriveApplicative`扩展?

时间:2013-09-17 22:57:08

标签: haskell ghc

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扩展?即使derivegeneric-derive软件包显然也缺少Applicative支持。是否有一个理论问题阻止这些实例通常有效(超出那些可能还会威胁FunctorFoldableTraversable扩展的原因?

3 个答案:

答案 0 :(得分:13)

对于遵循仿函数法则的给定数据类型,最多只有一个Functor实例。例如,mapfmap列表的唯一合法实现:

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,我们根本不会这样做。 FoldableTraversable通常有多个合法的实例,我们有一个标志来推导它们。有一段时间我们没有关于可穿越法律的真实好故事,但现在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)