stackage.org 上是否有针对 `liftA2` 和 `<*>` 的非循环定义

Geo*_*rge 2 haskell hoogle stackage

stackage.org,下面的循环声明存在liftA2<*>Applicative类型类。

       (<*>) = liftA2 id
liftA2 f x y = f <$> x <*> y
Run Code Online (Sandbox Code Playgroud)

是可用于现场liftA2<*>现场的非循环声明。这种完全循环的引用是一种疏忽吗?

更新:

hoogle文档中似乎缺少以下(必要的)澄清声明:

<*> :: Functor F => F (a -> b) -> F a -> F b
Run Code Online (Sandbox Code Playgroud)

并暗示(由于循环声明)

liftA2 :: Functor F => (a -> b -> c) -> F a -> F b -> F c
Run Code Online (Sandbox Code Playgroud)

Jon*_*rdy 6

[...] 存在以下循环定义用于liftA2<*>用于Applicative类型类。

       (<*>) = liftA2 id
liftA2 f x y = f <$> x <*> y
Run Code Online (Sandbox Code Playgroud)

是可用于站点liftA2<*>站点上的非循环定义。

确切地说,这些不是方法的定义;它们是默认定义。带有一个参数的类型类只是一组类型,而instance定义是该组成员资格的入场价格。在Minimal编译的Applicative告诉你,你必须实现这两种方法中的一种,并将该信息显示黑线鳕文档。

实际的定义liftA2<*>以及pure专用于的实例Applicative。一般来说,如果一个类型类包含只能使用该类型类的其他方法实现的方法,那么该方法并不严格需要是该类的一部分,因为它可以是具有约束的顶级定义。

然而,无论如何都可以包括这样的方法。这可能只是为了方便,当根据一个函数定义一个实例时更容易,即使它不是“最基本的”操作。这通常也是出于性能原因:当可以特定类型的默认方法更有效地实现方法时,往往会包含冗余方法。在这种情况下,例如,liftA2可以是能够在两个结构遍历在一起的效率比遍历一个与<$>然后其他分别与<*>

GHC 还提供DefaultSignatures了一种添加更多特定默认值的方法,通常用 定义Generic,但这只能让您添加类型类约束,主要是为了方便使用deriving.

这种完全循环的引用是一种疏忽吗?

完全没有,他们是故意的。类型类方法的默认实现中的循环定义非常常见。例如,Eq在 Haskell 报告中定义如下:

class Eq a where
  (==) :: a -> a -> Bool
  x == y = not (x /= y)

  (/=) :: a -> a -> Bool
  x /= y = not (x == y)
Run Code Online (Sandbox Code Playgroud)

可能忘记实现其中之一,因此它们都使用默认值,从而表示无限循环,但是:

  • 默认情况下会生成警告(-Wmissing-methods由 启用-Wdefault)。

  • 如果Minimal未指定编译指示,则假定类中的所有方法都是必需的。

因此,在这种情况下,实际上唯一的其他选项是从类中删除一个或另一个,或者省略为其中一个提供默认值。如果您想了解如何为 实现这些方法Applicative,则需要查看instance具体类型构造函数(如[]ZipListMaybeStateT等)的实现。

  • @George,你显然错过了*某事*,但并不完全清楚*什么*。也许您根本不理解类和实例是如何工作的?也许您不理解方法默认值?我不知道。 (2认同)