Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [152]

By Root 431 0
sense. But if something went wrong, it caused our whole program to crash. Now that we know how to make already existing code monadic, let’s take our RPN calculator and add error handling to it by taking advantage of the Maybe monad.

We implemented our RPN calculator by taking a string like "1 3 + 2 *", breaking it up into words to get something like ["1","3","+","2","*"]. Then we folded over that list by starting out with an empty stack and using a binary folding function that adds numbers to the stack or manipulates numbers on the top of the stack to add them together and divide them and such.

This was the main body of our function:

import Data.List

solveRPN :: String -> Double

solveRPN = head . foldl foldingFunction [] . words

We made the expression into a list of strings, and folded over it with our folding function. Then, when we were left with just one item in the stack, we returned that item as the answer. This was the folding function:

foldingFunction :: [Double] -> String -> [Double]

foldingFunction (x:y:ys) "*" = (y * x):ys

foldingFunction (x:y:ys) "+" = (y + x):ys

foldingFunction (x:y:ys) "-" = (y - x):ys

foldingFunction xs numberString = read numberString:xs

The accumulator of the fold was a stack, which we represented with a list of Double values. As the folding function went over the RPN expression, if the current item was an operator, it took two items off the top of the stack, applied the operator between them, and then put the result back on the stack. If the current item was a string that represented a number, it converted that string into an actual number and returned a new stack that was like the old one, except with that number pushed to the top.

Let’s first make our folding function capable of graceful failure. Its type is going to change from what it is now to this:

foldingFunction :: [Double] -> String -> Maybe [Double]

So, it will either return Just a new stack or it will fail with Nothing.

The reads function is like read, except that it returns a list with a single element in case of a successful read. If it fails to read something, it returns an empty list. Apart from returning the value that it read, it also returns the part of the string that it didn’t consume. We’re going to say that it always must consume the full input to work, and make it into a readMaybe function for convenience. Here it is:

readMaybe :: (Read a) => String -> Maybe a

readMaybe st = case reads st of [(x, "")] -> Just x

_ -> Nothing

Now let’s test it:

ghci> readMaybe "1" :: Maybe Int

Just 1

ghci> readMaybe "GOTO HELL" :: Maybe Int

Nothing

Okay, it seems to work. So, let’s make our folding function into a monadic function that can fail:

foldingFunction :: [Double] -> String -> Maybe [Double]

foldingFunction (x:y:ys) "*" = return ((y * x):ys)

foldingFunction (x:y:ys) "+" = return ((y + x):ys)

foldingFunction (x:y:ys) "-" = return ((y - x):ys)

foldingFunction xs numberString = liftM (:xs) (readMaybe numberString)

The first three cases are like the old ones, except the new stack is wrapped in a Just (we used return here to do this, but we could just as well have written Just). In the last case, we use readMaybe numberString, and then we map (:xs) over it. So, if the stack xs is [1.0,2.0], and readMaybe numberString results in a Just 3.0, the result is Just [3.0,1.0,2.0]. If readMaybe numberString results in a Nothing, the result is Nothing.

Let’s try out the folding function by itself:

ghci> foldingFunction [3,2] "*"

Just [6.0]

ghci> foldingFunction [3,2] "-"

Just [-1.0]

ghci> foldingFunction [] "*"

Nothing ghci> foldingFunction [] "1"

Just [1.0]

ghci> foldingFunction [] "1 wawawawa"

Nothing

It looks like it’s working! And now it’s time for the new and improved solveRPN. Here it is ladies and gents!

import Data.List

solveRPN :: String -> Maybe Double

solveRPN st = do

[result] <- foldM foldingFunction [] (words st)

return result

Just as in the previous version, we take the string and make it into a list of words. Then we do a fold, starting with the empty stack, but instead of

Return Main Page Previous Page Next Page

®Online Book Reader