5

让我们说,我有两个Maybe Bool价值,我想实现以下功能:

  • 如果两者都是Just值,我想||在它们之间执行值。
  • 如果其中一个是值,Nothing另一个是Just值,那么我希望该Just值作为输出。
  • 如果它们都是Nothing,那么我想要Just False作为输出。

我知道这可以使用模式匹配来实现。但是是否可以使用任何一元函数来获得结果?

liftM2适用于这种情况:

ghci> liftM2 (||) (Just True) (Just False)
Just True

但是当任何一个输入是(我想要另一个值)时liftM2会产生。IE:NothingNothingJust

ghci> liftM2 (||) (Nothing) (Just False)
Nothing

但我想Just False在上述情况下。

是否可以使用任何一元函数来做到这一点?

4

3 回答 3

9

就目前而言,我们甚至不需要调用一元装置。根据您的规范,“Nothing”可以映射到“False”,“Just b”可以映射到“b”:

mbor a b = Just (flat a || flat b)
   where flat = maybe False id

正如@leftaroundabout 正确指出的那样,这本质上就是 Monoid Any 实例所做的。

于 2013-12-09T12:27:47.747 回答
6

这里一个非常有用的运算符<|>来自Alternative. Control.Applicative对于Maybe,它的工作原理如下:

Just a  <|> _       = Just a
Nothing <|> Just a  = Just a
Nothing <|> Nothing = Nothing

我们还可以利用x || x == x始终正确的事实。这让我们可以编写以下内容:

orMaybe a b = liftA2 (||) (a <|> b) (b <|> a) <|> Just False

如果两者a都是bJust xliftA2 (||)结果为Just (a || b)。如果其中一个是Nothing,则(a <|> b)and(b <|> a)变成两者之一a或两者b,导致Just (a || a)or Just (b || b)。最后,如果两者都是Nothing,我们得到liftA2 (||) Nothing Nothing哪个导致Nothing。final<|> Just False然后将整个表达式转换为Just False.

现在,我认为这是一个有趣的练习。但我真的会使用这段代码吗?!因为Maybe,Nothing通常表示失败并传播;由于您使用的是一些非常非标准的行为,因此最好是显式地对所有情况进行模式匹配。

注:liftA2来自Control.Applicative. 就像liftM但对于应用程序一样;我用它来与<|>. 你也可以使用fmap

于 2013-12-09T12:24:32.457 回答
6

不,monad 实例没有 emptiness 1的概念,因此在这种情况下它无法检查Nothing和替换该值。

您基本上需要的是monoid实例;它具有Nothing作为它的标识元素,所以无论你结合什么,Nothing都会按原样出现。

instance (Monoid a) => Monoid (Maybe a)

不幸的Bool是,它本身并不是一个幺半群。好吧,实际上它是一个幺半群!但不是以独特的方式,所以他们不能选择任何特定的实例。但是使用newtype包装器,它们位于Data.Monoid

newtype Any = Any { getAny :: Bool }
instance Monoid Any

让我们试试吧……

Prelude Data.Monoid> fmap getAny $ (Just $ Any False) <‌> (Just $ Any False)
Just True
Prelude Data.Monoid> fmap getAny $ (Nothing) <‌> (Just $ Any False)
Just False


1当然,有fail……但那是历史性的意外。

于 2013-12-09T12:25:52.157 回答