Safe Reverse Polish Notation I implemented the following (what I believe to be) safe Reverse Polish Calculator.

Now I'm using foldM to take advantage of the power of Monads. Previously, I was not.

Can you please critique my code?

import Data.Maybe
import Text.Regex
import Control.Monad
import Network.CGI.Protocol

data Operator = Add | Subtract | Multiply

-- safe Reverse Polish Notation calculator
rpn :: String -&gt; Maybe Double
rpn xs = maybeStringToMaybeDouble result :: Maybe Double
      where result = safeHead $ foldM foldingOp [] $ words xs

--foldM :: Monad m =&gt; (a -&gt; b -&gt; m a) -&gt; a -&gt; [b] -&gt; m a

maybeStringToMaybeDouble :: Maybe String -&gt; Maybe Double
maybeStringToMaybeDouble x = do
      y &lt;- x
      maybeRead y 

-- extract maybeHead of Maybe [a]
safeHead :: Maybe [a] -&gt; Maybe a
safeHead m = do
    x &lt;- m -- x has type [a]
    listToMaybe x

foldingOp :: [String] -&gt; String -&gt; Maybe [String]
foldingOp acc s 
   | isNumber s        = Just (s : acc)
   | isPlusRegex s     = performOp Add acc
   | isMinusRegex s    = performOp Subtract acc
   | isMultiplyRegex s = performOp Multiply acc
   | otherwise         = Nothing

performOp :: Operator -&gt; [String] -&gt; Maybe [String]
performOp o (x:y:zs) = Just $ (doOp o x y) : zs
performOp _ _        = Nothing

doOp :: Operator -&gt; String -&gt; String -&gt; String
doOp Add a b       = show $ (read b :: Double) + (read a :: Double)
doOp Subtract a b  = show $ (read b :: Double) - (read a :: Double)
doOp Multiply a b  = show $ (read b :: Double) * (read a :: Double)


isNumber :: String -&gt; Bool
isNumber = matches' mkNumberRegex

mkNumberRegex :: Regex
mkNumberRegex = mkRegex "^[0-9]+[.]?[0-9]*$"

isPlusRegex :: String -&gt; Bool
isPlusRegex = matches' mkPlusRegex

isMinusRegex :: String -&gt; Bool
isMinusRegex = matches' mkMinusRegex

isMultiplyRegex :: String -&gt; Bool
isMultiplyRegex = matches' mkMultiplyRegex

mkPlusRegex :: Regex
mkPlusRegex = mkRegex "^[+]$"

mkMinusRegex :: Regex
mkMinusRegex = mkRegex "^[-]$"

mkMultiplyRegex :: Regex
mkMultiplyRegex = mkRegex "^[*]$"

matches' :: Regex -&gt; String -&gt; Bool
matches' r x 
   | matchRegex r x == Nothing = False
   | otherwise                 = True
This:


maybeStringToMaybeDouble x = do
      y &lt;- x
      maybeRead y



Could be:

maybeStringToMaybeDouble x = x &gt;&gt;= maybeRead


Similarly,


safeHead m = do
    x &lt;- m
    listToMaybe x



Could be

safeHead m = m &gt;&gt;= listToMaybe


Or we could remove these functions entirely and just write

rpn xs = foldM foldingOp [] (words xs) &gt;&gt;= listToMaybe &gt;&gt;= maybeRead


It seems strange that we're importing Network.CGI.Protocol for maybeRead. I would suggest using readMaybe from Text.Read instead.

There's a chance to use isJust here:


matches' :: Regex -&gt; String -&gt; Bool
matches' r x 
   | matchRegex r x == Nothing = False
   | otherwise                 = True



matches' :: Regex -&gt; String -&gt; Bool
matches' r x = isJust $ matchRegex r x


We can do away with six functions by rewriting foldingOp like this:

foldingOp :: [String] -&gt; String -&gt; Maybe [String]
foldingOp acc s 
   | isNumber s = Just (s : acc)
   | s == "+"   = performOp Add acc
   | s == "-"   = performOp Subtract acc
   | s == "*"   = performOp Multiply acc
   | otherwise  = Nothing


And we can simplify further by removing the Operator type:

foldingOp :: [String] -&gt; String -&gt; Maybe [String]
foldingOp acc s 
   | isNumber s = Just (s : acc)
   | s == "+"   = performOp (+) acc
   | s == "-"   = performOp (-) acc
   | s == "*"   = performOp (*) acc
   | otherwise  = Nothing

performOp :: (Double -&gt; Double -&gt; Double) -&gt; [String] -&gt; Maybe [String]
performOp o (x:y:zs) = Just $ doOp o x y : zs
performOp _ _        = Nothing

doOp :: (Double -&gt; Double -&gt; Double) -&gt; String -&gt; String -&gt; String
doOp op a b = show $ (read b :: Double) `op` (read a :: Double)