我对以下涉及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)
答案 0 :(得分:14)
==
是一个函数Eq a => a -> a -> Bool
。您正在制作其中一个操作数Nothing
,因此某些类型a
Maybe b
为b
,但Eq a
限制仍然适用 - {{ 1}}必须有Maybe b
个实例。 Eq
包含此类实例的定义 - Maybe b
- 适用于所有Eq (Maybe b)
。
换句话说,Eq b
只是一个函数,它不会提前知道您提供==
作为参数。它只知道它是某些 Nothing
,如果碰巧是Maybe 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
和Eq
的类型必须有x
个实例。
答案 1 :(得分:6)
因为==
的类型是
(==) :: (Eq a) => a -> a -> Bool
您的第二个定义(coalesce'
)使用==
,因此它会从Eq
继承对其参数的==
约束。
严格来说,coalesce'
对==
类型的值使用Maybe a
,但是有一个实例
instance (Eq a) => Eq (Maybe a) where ...
因此Eq (Maybe a)
约束变为Eq a
,因为==
上支持Maybe a
需要什么。
答案 2 :(得分:4)
让我们首先设计(==)
函数而不是Maybe
值。如果它们具有相同的数据构造函数,并且参数都相等,通常我们会认为两件事情是相同的。因此,如果F x1 x2 x3
和G y1 y2 y3
是相同的数据构造函数,F
,G
和{{{{1} 1}}。
因此,如果我们为x1 == y1
实现此功能,则有两种情况相同:两个x2 == y2
s和两个x3 == y3
s,其中值为Maybe
s封装是相同的,所以:
Nothing
是为Just
实施Just
实例的最合理方式。在实现中,我们使用instance Eq a => Eq (Maybe a) where
(==) Nothing Nothing = True
(==) (Just x) (Just y) = x == y
(==) _ _ = False
,因此我们添加了Eq
类型约束。
现在Haskell在概念上将功能视为黑盒子。对于Maybe a
,它会将x == y
视为Eq a
。如果您因此使用此Maybe
函数,则始终需要类型约束(==)
,无论特定用法是否需要此类型约束来执行(==) :: Eq a => Maybe a -> Maybe a -> Bool
检查。如果您编写(==)
,那么我们会看到,第二个子句(Eq a
)将永远不会被使用,但由于Haskell将函数视为黑盒,因此不知道类型约束在何种情况下是相关的。
isNothing :: Maybe a -> Bool
仅检查值是x == y
,如果是(== Nothing)
,则它始终为(==) (Just x) (Just y)
,无论值是{{1}构造函数换行,它实现如下:
Nothing
所以我们这里不需要Just
类型约束:我们不检查元素False
构造函数换行的相等性。因此,如果我们使用它,Haskell只会检查类型签名,注意到没有涉及Just
类型约束,因此不会将它添加到使用此函数的函数中。
请注意,您在此实施的内容实际上已在isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _ = False
模块中实现:(<|>) :: Alternative f => f a -> f a -> f a
,例如:
Eq a