Why is there no `
GHC has several useful language extensions for mechanically deriving various common Haskell typeclasses ( -XDeriveFunctor
, -XDeriveFoldable
, -XDeriveTraversable
). It seems that Applicative
is another class which is often needed and frequently easily derived. For a simple record containing slots of type a
, eg,
data SimpleRecord a = Simple a a a
the Applicative
instance is trivially derived,
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)
Even in the slightly harder case where some a
values are buried in other applicative functors, eg,
data MyRecord f a = MyRecord (f a) a
a reasonable instance is easily written,
instance (Applicative f) => Applicative (MyRecord f) where
pure x = MyRecord (pure x) x
MyRecord a1 b1 <*> MyRecord a2 b2 = MyRecord (a1 <*> a2) (b1 b1)
Why is it that a -XDeriveApplicative
extension implementing these sorts of mechanical instances does not exist? Even the derive
and generic-derive
packages apparently lack Applicative
support. Is there a theoretical issue precluding these instances from being usually valid (beyond those reasons that might also threaten the Functor
, Foldable
, or Traversable
extensions)?
There is at most one instance of Functor
for a given data type that follows the functor laws. For example, map
is the only lawful implementation of fmap
for lists:
fmap id == id
fmap (f . g) == fmap f . fmap g
But there can be more than one law-abiding instance of Applicative
, which isn't necessarily obvious.
pure id <*> v == v
pure (.) <*> u <*> v <*> w == u <*> (v <*> w)
pure f <*> pure x == pure (f x)
u <*> pure y == pure ($ y) <*> u
For lists, <*>
can behave like fs xs -> concatMap (f -> map f xs) fs
or like zipWith ($)
, and it isn't clear which one the compiler should choose.
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 fa
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.
链接地址: http://www.djcxy.com/p/43456.html上一篇: 为newtype创建MonadBaseControl实例
下一篇: 为什么没有`