[英]Why is Maybe's Semigroup instance biased towards Just and the Monoid uses Nothing as its empty element?
Maybe
表示可能由于错误而不会产生结果的计算。 因此,这种计算必须短路。
现在, Maybe
的Semigroup / Monoid实例似乎打破了这种语义,因为前者偏向于Just
而后者将错误情况视为Nothing
作为其空元素:
Just "foo" <> Nothing -- Just "foo"
Nothing <> Just "bar" -- Just "bar"
Just "foo" <> Just "bar" -- Just "foobar"
Nothing <> Nothing -- Nothing
对于前两种情况,我希望Nothing
。
这是替代实现(希望它是正确/合法的):
instance Semigroup a => Semigroup (Maybe a) where
Nothing <> _ = Nothing
_ <> Nothing = Nothing
Just a <> Just b = Just (a <> b)
instance Monoid a => Monoid (Maybe a) where
mempty = Just mempty
我不想说这些替代实例更好。 但它们似乎也很有用。 那么为什么首先进行选择而不是将实现留给用户呢?
您的实例实际上是应用函子的更通用实例的特例。
newtype LiftA f a = LiftA { getLiftA :: f a }
instance (Applicative f, Semigroup a) => Semigroup (LiftA f a) where
LiftA x <> LiftA y = LiftA $ liftA2 (<>) x y
instance (Applicative f, Monoid a) => Monoid (LiftA f a) where
mempty = LiftA $ pure mempty
我认为它会在某个地方的标准库中(可能在不同的名称下),但我找不到它。 但是这个一般实例的存在可能是选择Maybe
的库版本的一个原因,这更像是Maybe
的特殊能力。 另一方面,当你的代数结构彼此连贯时,它是相当不错的; 即当一个类型是Applicative
时,尽可能使用“ LiftA
”式实例(在所有F代数类上)。
在第三方面(!),我们无法在任何地方保持一致性,因为库实例与Maybe
的MonadPlus
实例一致。 这与自然数上存在两个幺半群的事实惊人地相似:加法和乘法。 对于数字,我们选择不要有任何 monoid实例,因为它不清楚使用哪个。
总之,我不知道。 但也许这些信息很有帮助。
我认为这背后的想法是, Maybe
应该具有实际上只有Option
正确表达的语义:它需要任何半群并通过添加Nothing
作为临时“免费mempty”而使其成为monoid。 即实际上应该是实例
instance Semigroup a => Semigroup (Maybe a) where
Nothing <> a = a
a <> Nothing = a
Just x <> Just y = Just $ x <> y
instance Semigroup a => Monoid (Maybe a) where
mappend = (<>)
mempty = Nothing
这类似于[]
通过添加mempty
和<>
给出任何类型的自由monoid。
当然,这要求Semigroup
类在base
,直到最近才出现。
对此的推论是, mempty
变得明显模式匹配,因为通过参数化它不能依赖于包含的类型。
实际上,这个实例可能比你提出的实例更有用,因为正如Luke所说, Applicative
实例已经涵盖了这个实例,所以你可以轻松编写liftA2 (<>)
和pure mempty
。 当然,标准实例也已经被Alternative
实例所覆盖,但是Alternative/MonadPlus
一直被认为有点hackish,不如数学上无可挑剔的Semigroup
, Monoid
, Functor
和Applicative
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.