Haskell 中 isNothing 和 (== Nothing) 的区别?

difference between isNothing and (== Nothing) in Haskell?

我很困惑为什么下面涉及 Nothing 的两个函数不同:

coalesce  m1 m2 = if  isNothing m1 then m2 else m1

coalesce' m1 m2 = if  (m1 == Nothing) then m2 else m1

第一个有类型:

λ> :t coalesce
coalesce :: Maybe a -> Maybe a -> Maybe a

符合预期。但是第二个有:

λ> :t coalesce'
coalesce' :: Eq a => Maybe a -> Maybe a -> Maybe a

为什么使用 (==Nothing) 会引入 Eq a 约束?

(GHC 8.2.2)

因为==的类型是

(==) :: (Eq a) => a -> a -> Bool

您的第二个定义 (coalesce') 使用 ==,因此它从 ==.

继承了对其参数的 Eq 约束

严格来说 coalesce' 对类型 Maybe a 的值使用 ==,但有一个实例

instance (Eq a) => Eq (Maybe a) where ...

所以 Eq (Maybe a) 约束变为 Eq a,因为这是在 Maybe a 上支持 == 所需要的。

== 是函数 Eq a => a -> a -> Bool。您正在创建其操作数之一 Nothing,因此 a 是某种类型 bMaybe b,但 Eq a 限制仍然适用 – Maybe b 必须有一个 Eq 实例。 Maybe b 包括对此类实例的定义 – Eq (Maybe b) – 对于所有 Eq b.

换句话说,== 只是一个函数,它不会提前知道您提供 Nothing 作为参数。它只知道是someMaybe a,如果碰巧是Just ….

就需要能够比较相等

换句话说,您可以通过以下方式定义 Maybe 已经存在的 ==

equals a b = case a of
    Nothing -> isNothing b
    Just x -> case b of
        Nothing -> False
        Just y -> x == y

注意 x == y 的出现方式,这意味着 xy.

类型必须有一个 Eq 实例

让我们首先针对 Maybe 值设计一个 (==) 函数。通常我们认为两个事物相同,如果它们具有相同数据构造函数,并且参数都相等。所以我们认为 F x1 x2 x3G y1 y2 y3 相同,如果 FG 是相同的数据构造函数,并且 x1 == y1x2 == y2x3 == y3.

所以如果我们为 Maybe 实现这个,有两种情况是相等的:两个 Nothing 和两个 Just,其中值 Justs封装相同,所以:

instance Eq a => Eq (Maybe a) where
    (==) Nothing Nothing = True
    (==) (Just x) (Just y) = x == y
    (==) _ _ = False

是为 Maybe a 实现 Eq 实例的最合乎逻辑的方式。在实现中,我们使用 x == y,因此我们添加了 Eq a 类型约束。

现在 Haskell 在概念上将函数视为黑盒。对于 Maybe,它因此将 (==) 视为 (==) :: Eq a => Maybe a -> Maybe a -> Bool。如果您因此使用此 (==) 函数,它将始终需要类型约束 Eq a,无论特定用法是否需要此类型约束来执行 x == y 检查。如果你写 (== Nothing),那么我们会看到,第二个子句 ((==) (Just x) (Just y)) 将永远不会被使用,但是由于 Haskell 将函数视为黑盒,它不知道在什么情况下类型约束是相关的。

isNothing :: Maybe a -> Bool 只检查值是否为 Nothing,如果是 Just,则始终为 False,无论值是什么 Just 构造函数包装,因此实现如下:

isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _ = False

所以我们在这里不需要 Eq a 类型约束:我们不检查元素 Just 构造函数换行的相等性。因此,如果我们再次使用它,Haskell 只会检查类型签名,注意到没有涉及 Eq a 类型约束,因此不会将其添加到使用此函数的函数中。

请注意,您在这里实现的实际上已经在 Control.Applicative 模块中实现:(<|>) :: Alternative f => f a -> f a -> f a,例如:

Prelude> import Control.Applicative
Prelude Control.Applicative> (<|>) Nothing Nothing
Nothing
Prelude Control.Applicative> (<|>) Nothing (Just 3)
Just 3
Prelude Control.Applicative> (<|>) (Just 3) Nothing
Just 3
Prelude Control.Applicative> (<|>) (Just 3) (Just 2)
Just 3