我在理解这段代码时缺少什么关键的直觉?我不明白这些是如何组成的。首先需要一个函数(a->c)和构造类型(fab)。然而,第二次产量(时尚)。此外,第一和第二是根据看起来循环的双图来定义的。
class Bifunctor f where
bimap :: (a -> c) -> (b -> d) -> f a b -> f c d
bimap g h = first g . second h
first :: (a -> c) -> f a b -> f c b
first g = bimap g id
second :: (b -> d) -> f a b -> f a d
second = bimap id
Run Code Online (Sandbox Code Playgroud)
second被咖喱化了。你可以写出隐含参数,它将与 对称first。
first g = bimap g id
second g = bimap id g
Run Code Online (Sandbox Code Playgroud)
至于你问的定义是否循环的问题,答案是肯定的。它们是圆形的。根据严格阅读Haskell标准,以下实现是正确的
data F a b = F a b
instance Bifunctor F -- No functions implemented; take all three defaults
Run Code Online (Sandbox Code Playgroud)
这将根据 Haskell 标准进行编译。但任何呼叫first、second、 或的尝试bimap都会触底,因为他们只会永远互相呼叫。
但是,如果您查看 的源代码Bifunctor,就会发现还有另一个小注释。
{-# MINIMAL bimap | first, second #-}
Run Code Online (Sandbox Code Playgroud)
这不是一个普通的评论。这是针对 GHC(最流行的 Haskell 编译器)的特殊指令。GHC 特别支持最小指令。这声明 的实例仅在至少Bifunctor实现或同时实现和 时才是完整的。因此,在 GHC 上,如果您自己的 未能满足该条件,您将收到编译器错误。这也反映在Hackage 文档中 bimapfirstsecondinstance
最小完整定义
Run Code Online (Sandbox Code Playgroud)bimap | first, second
我们期望,作为程序员,您可以覆盖其中一些默认值,而其余的默认值可以默默地就位。
您可以在内置类型类中更直接地看到相同的行为Eq
class Eq a where
(==), (/=) :: a -> a -> Bool
{-# INLINE (/=) #-}
{-# INLINE (==) #-}
x /= y = not (x == y)
x == y = not (x /= y)
{-# MINIMAL (==) | (/=) #-}
Run Code Online (Sandbox Code Playgroud)
和(==)都是(/=)相互实现的,并且MINIMAL底部有一个指令指示任何实例必须至少实现这两个函数之一。