You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

为何Maybe的Semigroup实例偏向Just,Monoid以Nothing为单位元?

解惑:Maybe的Semigroup/Monoid为何不按“错误短路”语义设计?

我太懂这种困惑了!刚接触Maybe的时候,直觉上总觉得它应该是「遇到错误(Nothing)就立刻短路返回」的,但标准库里它的Semigroup和Monoid实例行为确实和这个直觉冲突,对吧?

先再明确下你提到的实例行为:

Just "foo" <> Nothing -- Just "foo"
Nothing <> Just "bar" -- Just "bar"
Just "foo" <> Just "bar" -- Just "foobar"
Nothing <> Nothing -- Nothing

为什么会有这样的设计?

标准库的Maybe Semigroup/Monoid采用的是**「优先保留有效结果」**的语义,而不是「错误优先」的短路逻辑:

  • 只要两边有至少一个是Just,就会保留或合并这个有效内容;
  • 只有当两边都没有有效内容(都是Nothing)时,才返回Nothing

这种设计的场景其实很常见:比如你要合并多个可能缺失的配置项,希望能尽可能拿到可用的配置,而不是因为其中一项缺失就直接放弃所有。

你预期的“错误短路”语义怎么实现?

如果确实需要「遇到Nothing就立刻短路」的行为,完全可以自己定义一个新类型来实现这个语义:

-- 定义一个“失败即终止”的Maybe包装类型
newtype FailFast a = FailFast (Maybe a)

instance Semigroup a => Semigroup (FailFast a) where
  FailFast Nothing <> _ = FailFast Nothing  -- 左边出错直接返回
  _ <> FailFast Nothing = FailFast Nothing  -- 右边出错直接返回
  FailFast (Just x) <> FailFast (Just y) = FailFast (Just (x <> y))  -- 都有效则合并

-- 对应的Monoid实例
instance (Semigroup a, Monoid a) => Monoid (FailFast a) where
  mempty = FailFast (Just mempty)

这个实例就完全符合你预期的「错误短路」逻辑了。

关于Monoid单位元的选择

标准库把Nothing作为Maybe Monoid的单位元,其实是和它的Semigroup行为自洽的:
单位元必须满足x <> mempty = xmempty <> x = x,如果用Nothing当单位元:

  • Just "foo" <> memptyJust "foo" <> Nothing = Just "foo",符合;
  • mempty <> Just "foo"Nothing <> Just "foo" = Just "foo",符合;
    如果换成Just mempty当单位元,那Nothing <> Just mempty会得到Just mempty,而不是Nothing,就违反了单位元的定义。

简单来说,标准库的Maybe实例是「宽容型」的设计,而你预期的是「严格失败型」——两种语义各有用处,按需选择就好。

内容的提问来源于stack exchange,提问作者user6445533

火山引擎 最新活动