在stackage.org上,对于liftA2存在以下循环声明,对于Applicative类型类存在<*>循环声明。
(<*>) = liftA2 id
liftA2 f x y = f <$> x <*> y是站点上可用于liftA2或<*>的非循环声明。这种完全循环的引用是一种疏忽吗?
更新:
hoogle文档中似乎缺少以下(必要的)澄清声明:
<*> :: Functor F => F (a -> b) -> F a -> F b并且隐含(由于循环声明)
liftA2 :: Functor F => (a -> b -> c) -> F a -> F b -> F c发布于 2021-04-15 05:45:52
…对于
Applicative类型类的liftA2和<*>,存在以下循环定义。
( <*> ) = liftA2 id liftA2 f x y=f <$> x<*>y
是可用于站点上的liftA2或<*>的非循环定义。
确切地说,这些不是方法的定义;它们是默认的定义。只有一个参数的类型类只是一组类型,而instance定义是该集合中成员资格的入场费。Haddock的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)但是,可能会忘记实现其中的一个,因此它们都使用默认值,因此表示无限循环:
默认情况下,此选项会生成警告( -Wdefault).启用
Minimal杂注,则假定类中的所有方法都是必需的。因此,在这种情况下,实际上唯一的其他选择是从类中删除一个或另一个,或者省略为其中一个提供默认值。如果您想了解这些方法是如何为Applicative实现的,那么需要查看的是具体类型构造函数的instance实现,比如[]、ZipList、Maybe、StateT等等。
https://stackoverflow.com/questions/67098759
复制相似问题