如何减去 Haskell 中的 Maybe 值?例如,如果我想减去 Just 8 - Just 5 得到 Just 3,我该怎么做?

How do I subtract Maybe values in Haskell? For example, if I want to subtract Just 8 - Just 5 to get Just 3, how would I do that?

我怎样才能做到以下几点。例如,如果我想减去 Just 8 - Just 5 得到 Just 3,我该怎么做?

Just 8 - Just 5 = Just 3

Just 15 - Just 9 = Just 6
import Control.Applicative

main = print $ liftA2 (-) (Just 8) (Just 5)

liftA2 函数(从 Control.Applicative 导入)"lifts" 普通函数到 Applicative 上下文中。例如,liftA2 (-) 是一个函数,它不是采用两个 Num a => a 值,而是采用两个 (Applicative f, Num a) => f a 值,并在相同的上下文中生成结果。这不仅适用于 Maybe:

>>> liftA2 (-) (Just 8) (Just 5)
Just 3

但对于列表

>>> liftA2 (-) [4,5,6] [1,2,3] -- difference of every pair with one number from each list
[3,2,1,4,3,2,5,4,3]    

Either:

>>> liftA2 (-) (Right 8) (Right 5)
Right 3

函数

>>> liftA2 (-) (+3) (*2) 9 -- (\x -> (x + 3) - (x * 2)) 9 == 12 - 18
-6

等等

使用 monad 理解:

{-# LANGUAGE MonadComprehensions #-}

foo :: (Num b, Monad m) => m b -> m b -> m b
foo a b = [ x - y | x <- a, y <- b ]

不错,视觉效果好,被低估了。

foo (Just 8) (Just 5) returns Just 3.

当然,在这种简单的情况下,它在语法上是一种矫枉过正,在语义和实践上,使用 liftA2 (-) 会更好。

当您进行一些条件计算时,monad 理解真正发挥作用。经典示例是安全除法,避免 除零 错误:

safediv :: (MonadPlus m, Fractional b, Eq b) 
        => m b -> m b -> m b
safediv a b = [ x / y | x <- a, y <- b, y /= 0 ]

-- safediv (Just 8) (Just 0)  returns  Nothing

这是一个本质上 monadic 操作,它不能用应用程序编码,任何其他替代方式写下来肯定会更冗长。

一般来说,您应该使用单子方法:

Just 8 >>= \n -> Just 5 >>= \m -> return (n-m)

然而,在不涉及 monad 的情况下,我们仍然可以通过使用 Maybe 类型的仿函数和应用实例来完成这项工作。

首先让我们 fmapJust 8 值的 (-) 运算符,例如 (-) <$> Just 8。这将导致 Maybe 类型的应用程序,即 Just (8-) 类型签名 Just (8-) :: Num a => Maybe (a -> a)。现在只需一个应用程序操作即可完成工作:

λ> (-) <$> (Just 8) <*> (Just 5) 
Just 3

这有时是由 LiftA2 完成的,如其他一些答案所示,但我认为最好首先了解这种模式,因为 LiftA2 是硬连线到二元运算符/函数的,在其他情况下如果你必须解除一个三元运算符,比如 (,,) 那么你需要使用 LiftA3 来代替。然而,上面的模式只是以相同的方式进行,比如

λ> (,,) <$> Just 1 <*> Just 't' <*> Just "yeah"
Just (1,'t',"yeah")