简体   繁体   中英

Haskell - Left arrow vs nested case statements

I've unsuccessfully looked everywhere for a definition/description of this. While I was Haskell Programming from First Principles , in the monads intro chapter (pg. 763), it showed this example of a nested case statement:

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)

It said that it could be replaced by:

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)

How in the world does this work!? What is this called? The closest I could find is this answer , which describes it as "monadic notation".

A do expression is desugared to a chain of >>= functions. The Haskell report describes how to desugar do expressions. In your case, it means the expression is desugared to:

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

For Maybe the instance of Monad is implemented as [src] :

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

A Maybe can be seen as the result of a computation that can fail. The Nothing is the result of a computation that failed, and Just … as the result of the computation wrapped in a Just data constructor.

The instance of Monad enabes one to "chain" such computations. This means it will only return a Just … if all computations are successful (return a Just … ). So in your expression:

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)

So if one or more of the functions nonEmpty name' , nonNegative age' , nonNegative weight' and weightCheck (Cow nammy agey weighty) returns Nothing , the entire do block will evaluate to Nothing . The left side of the <- arrow is the result of the computation where the Just is unwrapped. This unwrapping is done on the instance of the Monad where we see (Just x) >>= k = … , where the Just data constructor is thus unwrapped, and x is used as parameter to the k function.

You can here make it more convenient by writing it as:

mkSphericalCow' :: String -> Int -> Int -> Maybe Cow
mkSphericalCow' name' age' weight' = () >>= weightCheck

Here we make use of the Functor and Applicative instance of Maybe where we thus will generate a Maybe Cow , that is a Just (Cow abc) given Just a = noEmpty name' , Just b = noNegative age' , Just c = noNegative weight' . Then we thus make use of the >>= function to unwrap the Just data constructor and pass the value wrapped in the Just to the weightCheck .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM