我刚刚发现自己在写这段代码:
import Control.Applicative ((<|>))
x = mA <|> mB <?> c
(<?>) :: Maybe a -> a -> a
Just x <?> _ = x
Nothing <?> y = y
其中mA :: Maybe a
, mB :: Maybe a
, c :: a
, x :: a
。基本上,代码说:选择第一个不是empty
的选项,默认为c
。您可以将其称为"反向Maybe monad",其中与<?>
类似的是pure
。
Just x = mA <|> mB <|> pure c,
但我对这种无可辩驳的模式感到不舒服。或者,当然,
x = fromMaybe c (mA <|> mB)
因为fromMaybe === flip <?>
.
<?>
运算符的灵感来自于parsec。当我发现自己定义这样的效用函数时,我总是感到怀疑,但我在任何地方都找不到这种默认行为。
显然Alternative
和Applicative
不够强大。
我错过了一个类型类吗?
我认为把事情留在(<?>) = flip fromMaybe
是一个好主意。
如果你想概括一下,Foldable
似乎是具有空概念的最简单的类:
(<?>) :: Foldable t => t a -> a -> a
ta <?> a = foldr const a ta
如果ta
为空或ta
的第一个元素,则返回a
。例子:
Just 0 <?> 10 == 0
Nothing <?> 0 == 0
[] <?> 10 == 10
实际上,我不喜欢您正在寻找的<?>
操作符名称。如果你在Stackage或Hayoo上搜索Maybe a -> a -> a
类型,你可以从errors
包中找到?:
操作符。
这个运算符叫做猫王-运算符。它在Groovy中以这种形式使用。Kotlin也有。这个运算符有助于处理命令式语言中的空值。但是,如果您想象Maybe a
是某种可空类型,那么?:
运算符对您来说也是有意义的。您可以观察到?:
操作符背后有一些历史。
此外,<?>
已经在一些软件包中使用,如megaparsec, attoparsec, optparse-generic等。你的项目很可能会用到其中的一个。因此,在使用您的elvis-operator版本时,您可能会遇到一些冲突。
我能想到的关于"空"的两个抽象概念是:
首先,MonadError
: Maybe
可以有instance MonadError () Maybe
。但请参见https://github.com/ekmett/mtl/issues/27
第二,lens
, _Empty
,这是默认的比较(Eq
)与mempty
(Monoid
)。然而Monoid
和Alternative
对Maybe
不一致。