18

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不存在实现这些机械实例的扩展?甚至deriveandgeneric-derive软件包显然也缺乏Applicative支持。是否存在阻止这些实例通常有效的理论问题(除了那些可能也威胁到FunctorFoldableTraversable扩展的原因)?

4

3 回答 3

15

对于给定的数据类型,最多有一个Functor遵循函子定律的实例。例如,for 列表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

对于列表,<*>可以表现为 like\fs xs -> concatMap (\f -> map f xs) fs或 like zipWith ($),并且不清楚编译器应该选择哪一个。

于 2013-09-17T23:04:44.587 回答
7

To echo others, there's no good reason I know of why we can't have -XDeriveApplicative, we simply happen not to. There are typically more than one lawful instance of Foldable and Traversable, and we have a flag to derive those. For a while we had no real good story for traversable laws, but now we have some. Similarly we still have no Foldable laws (but I think we can, see here).

Among the different 'obvious' applicatives, such as forwards and backwards ones (vis a vis <*> itself, and even permuted vis a vis the multiple a in an f a if there are such), then building applicatives as suggested here, with traversal in syntactic order, seems legitimate. However, for recursive types such as lists, or even types with multiple constructors, our choice of valid applicatives blossoms in interesting ways. For listlike regular recursive types, the obvious applicative choice is naturally the "zipLike" once, since it generalizes the nonrecursive case in the natural way, matching structure with structure. For sum types with multiple constructors, the "obvious" choice is much harder to define.

It seems to me that an entirely reasonable derivation of applicative would work as suggested here, in syntactic order on types (including recursive ones) with only one constructor. In cases of multiple constructors, failure seems legitimate.

On the other hand, while Foldable and Traversable seem to come up frequently in their "obvious" forms, its not too clear to me how many times we wish to define "obvious" applicatives, as compared to interesting ones. My intuition tells me this feature would be seldom-exercised, and perhaps simply not frequently useful.

于 2013-09-26T03:58:24.060 回答
3

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)
于 2021-08-04T11:56:26.340 回答