[Describe do-syntax desugaring. Bryan O'Sullivan **20071220055631] { addfile ./examples/ch14/Do.hs addfile ./examples/ch14/do.ghci hunk ./en/ch07-io.xml 70 - of the last action executed. + of the last action executed. For a complete description of &do; + syntax, see . hunk ./en/ch14-monads.xml 666 - is under the control of the monad's implementor, not something - that's shared by all monads. Indeed, some monads come in - multiple flavours, each with different levels of + is under the control of the monad's implementor. It's not a + constant that's shared by all monads. Indeed, some monads + come in multiple flavours, each with different levels of hunk ./en/ch14-monads.xml 675 - Blorp. + + Haskell's &do; syntax is an example of syntactic + sugar: it provides an alternative way of writing + monadic code, without using &bind; and anonymous functions. + Desugaring is the translation of syntactic + sugar back to the core language. + + The rules for desugaring a &do; block are easy to follow. We + can think of a compiler as applying these rules mechanically and + repeatedly to a &do; block until no more &do; keywords + remain. + + A &do; keyword followed by a single action is translated to + that action by itself. + + + + + + &Do.hs:doNotation1; + &Do.hs:translated1; + + + + + + A &do; keyword followed by more than one action is + translated to the first action, then &bind_;, followed by a &do; + keyword and the remaining actions. When we apply this rule + repeatedly, the entire &do; block ends up chained together by + applications of &bind_;. + + + + + + &Do.hs:doNotation2; + &Do.hs:translated2; + + + + + + The &larrow; notation has a translation that's worth paying + close attention to. On the left of the &larrow; is a normal + Haskell pattern. This can be a single variable or something more + complicated. A guard expression is not allowed. + + + + + + &Do.hs:doNotation3; + &Do.hs:translated3; + + + + + + This pattern is translated into a &let; binding that + declares a local function with a unique name (we're just using + f as an example above). The action on the + right of the &larrow; is then chained with this function using + &bind;. + + What's noteworthy about this translation is that if the + pattern match fails, the local function calls the monad's &fail; + implementation. Here's an example using the Maybe + monad. + + &Do.hs:robust; + + The &fail; implementation in the Maybe monad + simply returns Nothing. If the pattern match in + the above function fails, we thus get Nothing as + our result. + + &do.ghci:robust; + + Finally, when we write a &let; expression in a &do; block, + we can omit the usual ∈ keyword. Subsequent actions in the + block must be lined up with the &let; keyword. + + + + + + &Do.hs:doNotation4; + &Do.hs:translated4; + + + + + + + Monads as a programmable semicolon + + Back in , we + mentioned that layout is the norm in Haskell, but it's not + required. We can write a &do; block + using explicit structure instead of layout. + + &Do.hs:semicolon; + + Even though this use of explicit structure is rare, it has + given rise to the description of monads as a kind of + programmable semicolon, because the behaviours + of &bind_; and &bind; are different in each monad. + + &Do.hs:semicolonTranslated; + + hunk ./examples/ch14/Do.hs 1 +act = undefined +act1 = undefined +act2 = undefined +actN = undefined + +expr1 = undefined +expr2 = undefined +exprN = undefined + +doNotation1, doNotation2, doNotation3, doNotation4 :: Maybe () +translated1, translated2, translated3, translated4 :: Maybe () +finalTranslation2, semicolon, semicolonTranslated :: Maybe () + +{-- snippet doNotation1 --} +doNotation1 = + do act +{-- /snippet doNotation1 --} + +{-- snippet translated1 --} +translated1 = + act +{-- /snippet translated1 --} + +{-- snippet doNotation2 --} +doNotation2 = + do act1 + act2 + {- ... etc. -} + actN +{-- /snippet doNotation2 --} + +{-- snippet translated2 --} +translated2 = + act1 >> + do act2 + {- ... etc. -} + actN + +finalTranslation2 = + act1 >> + act2 >> + {- ... etc. -} + actN +{-- /snippet translated2 --} + +{-- snippet doNotation3 --} +doNotation3 = + do pattern <- act1 + act2 + {- ... etc. -} + actN +{-- /snippet doNotation3 --} + +{-- snippet translated3 --} +translated3 = + let f pattern = do act2 + {- ... etc. -} + actN + f _ = fail "..." + in act1 >>= f +{-- /snippet translated3 --} + +{-- snippet doNotation4 --} +doNotation4 = + do let val1 = expr1 + val2 = expr2 + {- ... etc. -} + valN = exprN + act1 + act2 + {- ... etc. -} + actN +{-- /snippet doNotation4 --} + +{-- snippet translated4 --} +translated4 = + let val1 = expr1 + val2 = expr2 + valN = exprN + in do act1 + act2 + {- ... etc. -} + actN +{-- /snippet translated4 --} + +{-- snippet robust --} +robust :: [a] -> Maybe a +robust xs = do (_:x:_) <- Just xs + return x +{-- /snippet robust --} + +{-- snippet semicolon --} +semicolon = do + { + act1; + val1 <- act2; + let { val2 = expr1 }; + actN; + } +{-- /snippet semicolon --} + +{-- snippet semicolonTranslated --} +semicolonTranslated = + act1 >> + let f val1 = let val2 = expr1 + in actN + f _ = fail "..." + in act2 >>= f +{-- /snippet semicolonTranslated --} hunk ./examples/ch14/do.ghci 1 +--# robust +robust [1,2,3] +robust [1] }