haskell 为什么没有“-XDeriveApplicationative”扩展?

vc9ivgsu  于 2022-11-14  发布在  其他
关注(0)|答案(3)|浏览(137)

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支持。是否有理论上的问题阻碍了这些示例的有效性(除了那些可能威胁到FunctorFoldable,或Traversable扩展的原因之外)?

ngynwnxp

ngynwnxp1#

对于给定的数据类型,最多只能有一个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) fszipWith ($)那样工作,并且不清楚编译器应该选择哪一个。

tzcvj98z

tzcvj98z2#

为了呼应其他人的观点,我不知道为什么我们不能有-XDeriveApplicative,我们只是碰巧没有。通常有不止一个FoldableTraversable的合法示例,我们有一个标志来派生这些示例。有一段时间,我们没有关于可遍历定律的真实的好故事,但是现在we have some。同样地,我们仍然没有Foldable定律(但是我认为我们 * 可以 *,参见here)。
在不同的“明显”应用句中,例如向前和向后的应用句(维斯a vis <*>本身,甚至vis a vis f a中的多个a,如果存在这样的情况),那么按照这里的建议构建应用程序,并按语法顺序遍历,似乎是合法的。我们对有效应用的选择以有趣的方式展开。对于列表式的正则递归类型,明显的应用选择自然是“zipLike”一次,因为它以自然的方式推广了非递归情况,使结构与结构匹配。对于具有多个构造函数的求和类型,“明显的”选择要难定义得多。
在我看来,一个完全合理的applicative派生应该像这里所建议的那样工作,按照只有一个构造函数的类型(包括递归类型)的语法顺序。在多个构造函数的情况下,失败似乎是合理的。
另一方面,虽然Foldable和Traversable似乎经常以“显而易见”的形式出现,但我不太清楚,与有趣的形式相比,我们有多少次希望定义“显而易见”的应用。我的直觉告诉我,这个特性很少被使用,也许只是不经常有用。

polkgigr

polkgigr3#

下一个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)

这只适用于乘积类型,求和类型的应用性要多样化得多,因为我们需要一种连贯的方式来协调构造函数之间的关系。这需要一个函数来保持应用性结构(应用性态射),在我写的这个包中称为Idiom
https://hackage.haskell.org/package/idiomatic
这个包允许派生具有应用态射的类型级别配置的总和类型的应用。

相关问题