Reading Do Notation as Nested Binds
Be aware of where the parenthesis appear when using multiple bind expressions (e.g.
m a >>= aToMB >>= bToMC). Below provides a summary of the section called "Do notation" in this article:
data Maybe a = Nothing | Just a instance Bind Maybe where bind :: forall a b. Maybe a -> (a -> Maybe b) -> Maybe b -- when given a Nothing, stop all future computations and return immediately. bind Nothing _ = Nothing -- when given a Just, run the function on its contents bind (Just a) f = f a half :: Int -> Maybe Int half x | x % 2 == 0 = Just (x / 2) | otherwise = Nothing -- This statement (Just 128) >>= half >>= half >>= half -- desugars first to (Just 128) >>= (\original -> half original >>= half >>= half ) -- which can be better understood as (Just 128) >>= aToMB -- which can be better understood as bind (Just 128) >>= aToMB -- since the latter ">>=" calls are nested inside of the first one, one -- should read the above computation as "Only continue if the previous -- `bind`/`>>=` call was successful." -- In this situation, it is: bind (Just 128) (\original -> half original >>= half >>= half) -- reduces to (\128 -> half 128 >>= half >>= half) -- reduces to half 128 >>= half >>= half -- ... and so forth until we get the result: Just 16 -- Similarly Nothing >>= half >>= half >>= half == Nothing -- desguars first to Nothing >>= (\value -> half value >>= half >>= half) == Nothing -- which can be better understood as Nothing >>= aToMB == Nothing -- which can be better understood as bind Nothing aToMB == Nothing -- and, looking at the instance of Bind above, reduces to Nothing -- The other `half` computations are never executed. -- Thus, given this function... half3Times :: Maybe Int -> Maybe Int half3Times maybeI = do original <- maybeI first <- half original -- === second <- half first -- | a -> m b third <- half second -- | pure third -- === -- ... passing in `Nothing` doesn't compute anything half3Times Nothing == Nothing -- Likewise, passing in a bad starting value will also stop the computation -- as soon as possible: (Just 3) >>= half >>= (\thisWontRun -> pure thisWontRun) -- will desugar to bind (Just 3) half = -- will desugar to half 3 -- which desugars to half 3 | 3 % 2 == 0 | otherwise = Nothing -- which tests whether `3 % 2 == 0` (false) the 'otherwise path' Nothing >>= (\thisWontRun -> pure thisWontRun) -- which desugars to bind NOthing (\thisWontRun -> pure thisWontRun) -- which desugars to Nothing