I wrote this code to learn how to work with State monad. Then I added Either result to gracefully handle error condition (stack underflow).
I believe most of this code is actually unnecessary. I know there are stateful and monadic lens
out there, there is EitherT
transformer and also StateStack
monad. How can I go about incorporating those libraries into this example?
For example, I imagine that it is possible to replace safely
with %=
or mapMOf
from lens
library once appropriate EitherT monad stack is constructed but I'm not sure how to get there.
{-# LANGUAGE LambdaCase #-}
module Main where
import Control.Lens
import Control.Monad.State
push :: a -> State [a] ()
push a = get >>= put . (a:)
pop :: State [a] (Either String a)
pop = get >>= \case (x:xs) -> liftM (const $ Right x) $ put xs
_ -> return $ Left "Stack underflow"
safely :: (a -> a) -> (Either String a) -> State [a] ()
safely f = either (const $ return ()) (push . f)
-- Stack operations that cannot crash program.
--
-- To avoid passing stack back and forth in pure language, use State monad.
-- To avoid `if` statements for overflow checking, use Either monad.
-- To avoid code duplication use parameterized and high-order types.
main :: IO ()
main = do
print $ runState (push "Hello" >> pop >> pop >>= safely (++" World!") >> pop) []
print $ runState (push 2 >> pop >>= safely (**7) >> pop) []