7

Maybe表示可能由于错误而不会产生结果的计算。因此,这样的计算必须是短路的。

NowMaybe的 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

我不想说这些替代实例更好。但它们似乎也很有用。那么为什么首先做出选择而不是将实现留给用户呢?

4

2 回答 2

8

您的实例实际上是应用函子的更通用实例的特例。

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使用“ ”样式的实例(在所有F 代数类上)。LiftA

另一方面(!),我们不可能在任何地方都有连贯性,因为库实例与Maybe'sMonadPlus实例一致。这与自然数上有两个幺半群的事实惊人地相似:加法和乘法。对于数字,我们只是选择不使用任何monoid 实例,因为不清楚使用哪个。

总之,我不知道。但也许这些信息是有帮助的。

于 2018-05-04T09:05:29.997 回答
4

我认为这背后的想法是,Maybe它应该具有实际上只正确表达的语义Option:它采用任何半群并从中生成一个幺半群,通过添加Nothing作为临时的“free mempty”。即实际上实例应该是

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<>

当然,这要求Semigroup该类在 中base,直到最近才出现。

对此的推论是,它mempty变得明显是模式匹配的,因为通过参数化它不能依赖于包含的类型。

实际上,这个实例可以说比你提出的那个更有用,因为正如 Luke 所说,Applicative实例已经涵盖了它,所以你可以轻松地编写liftA2 (<>)and pure mempty. 诚然,标准实例也已经被Alternative实例所覆盖,但Alternative/MonadPlus一直被认为有点骇人听闻,不如在数学上更无可挑剔SemigroupMonoid,FunctorApplicative.

于 2018-05-04T12:59:38.450 回答