Haskell - 左箭头与嵌套的 case 语句

Haskell - Left arrow vs nested case statements

我到处找 definition/description 都没有成功。当我 Haskell 从第一原则编程 时,在 monads 介绍章节(第 763 页)中,它显示了嵌套 case 语句的示例:

mkSphericalCow :: String -> Int -> Int -> Maybe Cow
mkSphericalCow name' age' weight' =
    case noEmpty name' of
        Nothing -> Nothing
        Just nammy ->
            case noNegative age' of
                Nothing -> Nothing
                Just agey ->
                    case noNegative weight' of
                        Nothing -> Nothing
                        Just weighty ->
                            weightCheck
                                (Cow nammy agey weighty)

上面说可以替换为:

mkSphericalCow' :: String -> Int -> Int -> Maybe Cow
mkSphericalCow' name' age' weight' = do
    nammy <- noEmpty name'
    agey <- noNegative age'
    weighty <- noNegative weight'
    weightCheck (Cow nammy agey weighty)

这到底是怎么回事!?这个叫什么?我能找到的最接近的是 this answer,它描述为“一元表示法”。

一个do表达式被脱糖为一个>>=函数链。 Haskell report 描述了如何对 do 表达式进行脱糖。在您的情况下,这意味着表达式被脱糖为:

mkSphericalCow' :: String -> Int -> Int -> Maybe Cow
mkSphericalCow' name' age' weight' = noEmpty name' >>= (\nammy -> noNegative age' >>= (\agey -> nonNegative weight' >>= (\weighty -> weightCheck (Cow nammy agey weighty))))

对于 Maybe Monad 的实例是 implemented as [src]:

instance  Monad Maybe  where
    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

A Maybe 可以看作是一个可能失败的计算结果。 Nothing 是计算失败的结果,Just … 是包装在 Just 数据构造函数中的计算结果。

Monad 的实例使人们能够“链接”此类计算。这意味着它只会 return a Just … 如果所有计算都成功(return a Just …)。所以在你的表达中:

mkSphericalCow' :: String -> Int -> Int -> Maybe Cow
mkSphericalCow' name' age' weight' = do
    nammy <- noEmpty name'
    agey <- noNegative age'
    weighty <- noNegative weight'
    weightCheck (Cow nammy agey weighty)

所以如果函数 nonEmpty name'nonNegative age'nonNegative weight'weightCheck (Cow nammy agey weighty) returns Nothing 中的一个或多个,则整个do 块将计算为 Nothing<- 箭头的左侧是 Just 展开的计算结果。这种解包是在我们看到 (Just x) >>= k = …Monad 实例上完成的,其中 Just 数据构造函数因此被解包,并且 x 用作 k函数。

这里可以这样写更方便:

mkSphericalCow' :: String -> Int -> Int -> Maybe Cow
mkSphericalCow' name' age' weight' = (<b>Cow <$> noEmpty name' <*> nonNegative age' <*> noNegative weight'</b>) >>= weightCheck

这里我们使用 MaybeFunctorApplicative 实例,因此我们将生成 Maybe Cow,即给定的 Just (Cow a b c) Just a = noEmpty name'Just b = noNegative age'Just c = noNegative weight'。然后我们利用 >>= 函数解包 Just 数据构造函数并将包装在 Just 中的值传递给 weightCheck.