[More ch20 work John Goerzen **20080429061927] { addfile ./examples/ch20/divby3.ghci addfile ./examples/ch20/divby4.ghci addfile ./examples/ch20/divby4.hs addfile ./examples/ch20/divby5.ghci addfile ./examples/ch20/divby5.hs hunk ./en/ch20-errors.xml 159 - + Take a look at this function. We're back to using ↦, + which is a good thing for both laziness and simplicity. We + can try it out in &ghci; and see that it works for finite + and infinite lists just fine: + + &divby3.ghci:ex1; + + We hope that you can take from this discussion the point + that there is a distinction between the input not being + well-formed (as in the case of safeTail) + and the input potentially containing some bad data, as in + the case of divBy. These two cases can + often justify different handling of the results. + + + + Usage of the Maybe Monad + + Back in , we had an example + program named divby2.hs. This example + didn't preserve laziness, but returned a value of type + Maybe [a]. The exact same algorithm + could be expressed using a monadic style. For more + information and important background on monads, please refer + to . Here's our new monadic-style + algorithm: + + &divby4.hs:all; + + The &Maybe; monad has made the expression of this algorithm + look nicer. For the &Maybe; monad, &return; is the same as + &Just;, and fail _ = Nothing, so our + error explanation string is never actually seen anywhere. + We can test this algorithm with the same tests we used + against divby2.hs if we want: + + &divby4.ghci:ex1; + + + The code we wrote actually isn't specific to the &Maybe; + monad. By simply changing the type, we can make it work for + any monad. Let's try it: + + &divby5.ghci:all; + + The function divByGeneric contains the + same code as divBy did before; we just + gave it a more general type. This is, in fact, the type + that &ghci; infers if no type would be given. We also + defined a convenience function divBy with + a more specific type. + + + Let's try this out in &ghci;. + + hunk ./examples/ch20/divby3.ghci 1 +:l divby3.hs +--# ex1 +divBy 50 [1,2,5,8,10] +divBy 50 [1,2,0,8,10] +take 5 (divBy 100 [1..]) hunk ./examples/ch20/divby4.ghci 1 +:l divby4.hs +--# ex1 +divBy 50 [1,2,5,8,10] +divBy 50 [1,2,0,8,10] +divBy 100 [1..] hunk ./examples/ch20/divby4.hs 1 +{-- snippet all --} +-- ch20/divby4.hs + +divBy :: Integral a => a -> [a] -> Maybe [a] +divBy _ [] = return [] +divBy _ (0:_) = fail "division by zero in divBy" +divBy numerator (denom:xs) = + do next <- divBy numerator xs + return ((numerator `div` denom) : next) +{-- /snippet all --} hunk ./examples/ch20/divby5.ghci 1 +--# ex1 +:l divby5.hs +(divByGeneric 50 [1,2,5,8,10])::(Integral a => Maybe [a]) +divByGeneric 50 [1,2,5,8,10] +divByGeneric 50 [1,2,0,8,10] +--# ex2 +:m +Control.Monad.Error +(divByGeneric 50 [1,2,5,8,10])::(Integral a => Either String [a]) +(divByGeneric 50 [1,2,0,8,10])::(Integral a => Either String [a]) + hunk ./examples/ch20/divby5.hs 1 +{-- snippet all --} +-- ch20/divby5.hs + +divBy :: Integral a => a -> [a] -> Maybe [a] +divBy = divByGeneric + +divByGeneric :: (Monad m, Integral a) => a -> [a] -> m [a] +divByGeneric _ [] = return [] +divByGeneric _ (0:_) = fail "division by zero in divByGeneric" +divByGeneric numerator (denom:xs) = + do next <- divByGeneric numerator xs + return ((numerator `div` denom) : next) +{-- /snippet all --} }