We have seen the RPN can be solved with haskell in previous examples, now we will see how we can do use monad to solve it.
With monad, you might not gain more in terms of just solving hte problem, but you will get more like logging ability or, graceful failure or somehting else.
So, here is a example of how you can leverage the use of Monad to give more context information on the solution.
import Data.List solveRPN :: String -> Double solveRPN = head . foldl foldingFunction [] . words
as before this is what we have for the main body of the solution,
foldingFunction :: [Double] -> String -> [Double] foldingFunction (x:y:ys) "*" = (x * y):ys foldingFunction (x:y:ys) "+" = (x + y):ys foldingFunction (x:y:ys) "-" = (y - x):ys foldingFunction xs numberString = read numberString:xs
now, we are looking to add graceful failure to the function, and we might change the signature to something like this:
foldingFunction :: [Double] -> String -> Maybe [Double]
so we will return Just a new stack if success, otherwise we will return Nothing.
we nees some assiisting functions. like the readMaybe,
readMaybe :: (Read a) => String -> Maybe a readMaybe st = case reads st of [(x,"")] -> Just x _ -> Nothing
to test that with the interactive console..
ghci> readMaybe "1" :: Maybe Int Just 1 ghci> readMaybe "GO TO HELL" :: Maybe Int Nothing
so with that , readMaybe can return failure on some input that it is not valid. now, we can rewrite the code ..
foldingFunction :: [Double] -> String -> Maybe [Double] foldingFunction (x:y:ys) "*" = return ((x * y):ys) foldingFunction (x:y:ys) "+" = return ((x + y):ys) foldingFunction (x:y:ys) "-" = return ((y - x):ys) foldingFunction xs numberString = liftM (:xs) (readMaybe numberString)
and we will ignore those succeess cases and we will only show the failure case. here is what we might get for a invalid ..
ghci> foldingFunction [] "1" Just [1.0] ghci> foldingFunction [] "1 wawawawa" Nothing
so with this, we will wrap it up and here is waht we can get ..
import Data.List solveRPN :: String -> Maybe Double solveRPN st = do [result] <- foldM foldingFunction [] (words st) return result
so instead of normal foldl, we do a foldM, so we will give a shot...
ghci> solveRPN "1 2 * 4 +" Just 6.0 ghci> solveRPN "1 2 * 4 + 5 *" Just 30.0 ghci> solveRPN "1 2 * 4" Nothing ghci> solveRPN "1 8 wharglbllargh" Nothing
Now that we have seen that we can solve some problems with monad , like the above we used that to solve the reverseRPN problem, now let's see how ew can compose monadic functions,
first let's review some function composition.
ghci> let f = (+1) . (*100) ghci> f 4 401 ghci> let g = (\x -> return (x+1)) <=< (\x -> return (x*100)) ghci> Just 4 >>= g Just 401
so you can see that instead of just . function composition, we use the >=> , but instead of id as the starting accumulator, and the . notatin, we uses "return" and "<=<" insteads. so
ghci> let f = foldr (.) id [(+1),(*100),(+1)] ghci> f 1 201
can be translated to somethng else.
so let's divert to something else, rember that we have solved some problem like moveKnight questions.
in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight
and we can check that if we can reach to some position in three moves
canReachIn3 :: KnightPos -> KnightPos -> Bool canReachIn3 start end = end `elem` in3 start
with monad, we can know not just three, but any numbers of moves, let's make it more general , here is the code.
import Data.List inMany :: Int -> KnightPos -> [KnightPos] inMany x start = return start >>= foldr (<=<) return (replicate x moveKnight)
as we know that we have return instead of "id"..
and we can make it more general , here is what we will get.
canReachIn :: Int -> KnightPos -> KnightPos -> Bool canReachIn x start end = end `elem` inMany x start