首页 文章

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

提问于
浏览
15

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 扩展的原因)?

2 回答

  • 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 ($) ,并且不清楚编译器应该选择哪一个 .

  • 5

    为了回应别人,有's no good reason I know of why we can',我们根本就没有 . 通常有多个 FoldableTraversable 的合法实例,我们有一个标志来推导它们 . 有一段时间我们没有关于可穿越法律的真实好故事,但现在we have some . 同样,我们仍然没有 Foldable 法律(但我认为我们可以,见here) .

    在不同的'obvious'应用程序中,例如向前和向后的应用程序(相对于 <*> 本身,甚至在 f a 中如果有这样的情况下进行置换),然后按照句法顺序遍历,如此处所 Build 的应用程序,看似合法 . 但是,对于诸如列表之类的递归类型,甚至是具有多个构造函数的类型,我们选择有效的应用程序都会以有趣的方式开花 . 对于类似列表的常规递归类型,明显的应用选择自然是"zipLike"一次,因为它以自然的方式推广非递归情况,将结构与结构匹配 . 对于具有多个构造函数的sum类型,"obvious"选项更难定义 .

    在我看来,完全合理的applicative派生将按照这里的建议工作,在语法顺序上只有一个构造函数的类型(包括递归的) . 在多个构造函数的情况下,失败似乎是合法的 .

    另一方面,虽然可折叠和可穿越似乎经常出现在他们的“明显”形式中,但对于我来说,与有趣的相比,我们希望定义“明显”的应用程序多少次 . 我的直觉告诉我,这个功能很少被运用,也许根本不常用 .

相关问题