Why is there no `-XDeriveApplicative` extension?

家住魔仙堡 提交于 2019-12-30 08:04:08

问题


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, e.g.,

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, e.g.,

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)?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/18861231/why-is-there-no-xderiveapplicative-extension

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!