Short version: when I use runMaybeT
and then runState
on a monad of type MaybeT (State <type>) ()
, it looks like no state changes occur even though the Maybe
result is equal to Just ()
. Why?
Full version: I'm writing a program to solve the Towers of Hanoi. I represent the solution as a list of State
monads that, when sequenced, manipulate an initial Towers
state:
data Towers = Towers [Int] [Int] [Int]
deriving (Show)
type Move = State Towers ()
towerMoves :: Int -> Rod -> Rod -> [Move]
towerMoves 1 r1 r2 = [pop r1 >>= push r2]
towerMoves n r1 r2 = topToTemp ++ (towerMoves 1 r1 r2) ++ topToFinal
where
r3 = other r1 r2
topToTemp = towerMoves (n - 1) r1 r3
topToFinal = towerMoves (n - 1) r3 r2
moves = towerMoves 5 First Third
initTowers = Towers [1,2,3,4,5] [] []
main = print $ snd $ runState (sequence_ moves) initTowers
Thus far, this program produces the correct output:
Towers [] [] [1,2,3,4,5]
Then, I wanted to verify that the program respected the rules of the puzzle, namely that no larger disc (represented by numbers here) comes before a smaller disc. I wanted to insert some kind of verification after every Move
, so I tried to use the MaybeT
monad transformer to send a failure down the list of moves:
verifiedMoves :: [MaybeT (State Towers) ()]
verifiedMoves = map ((>> verify) . return) moves
where
check :: [Int] -> Bool
check [] = True
check [_] = True
check (x:y:ys) = (x < y) && check (y:ys)
verify :: MaybeT (State Towers) ()
verify = do
(Towers xs ys zs) <- lift get
guard (check xs && check ys && check zs)
Accordingly I changed the main
monad:
main = maybe (putStrLn "violation") (const $ print finalTowers) v
where
(v, finalTowers) = runState (runMaybeT $ sequence_ verifiedMoves) initTowers
Now the output looks wrong, like no state changes occurred:
Towers [1,2,3,4,5] [] []
If I make the initial state invalid, it indeed fails the verification. So if there is no state change because the effects of the Move
s got interrupted, I would expect the output to be the word "violation."
Why, after applying runMaybeT
, is the result of applying runState
equal to (Just (), Towers [1,2,3,4,5] [] [])
?
Here is the rest of the code, for reference. I tried lifting the get
and put
monads in my pop
and push
functions, but that produced the same output.
import Control.Monad
import Data.Functor.Identity
import Control.Monad.State
import Control.Monad.Trans.Maybe
import qualified Data.Map as M
data Rod = First | Second | Third
deriving (Show)
other :: Rod -> Rod -> Rod
other First Second = Third
other Second First = Third
other First Third = Second
other Third First = Second
other Second Third = First
other Third Second = First
getRod :: Towers -> Rod -> [Int]
getRod (Towers x y z) First = x
getRod (Towers x y z) Second = y
getRod (Towers x y z) Third = z
setRod :: Rod -> Towers -> [Int] -> Towers
setRod First t ds = Towers ds r2 r3
where
r2 = t `getRod` Second
r3 = t `getRod` Third
setRod Second t ds = Towers r1 ds r3
where
r1 = t `getRod` First
r3 = t `getRod` Third
setRod Third t ds = Towers r1 r2 ds
where
r1 = t `getRod` First
r2 = t `getRod` Second
pop :: Rod -> State Towers Int
pop r = do
t <- get
let ds = t `getRod` r
d = head ds
load = setRod r
put $ t `load` (tail ds)
return d
push :: Rod -> Int -> State Towers ()
push r d = do
t <- get
let ds = t `getRod` r
load = setRod r
put $ t `load` (d:ds)
Look at this line
verifiedMoves = map ((>> verify) . return) moves
equivalent to
= map (\m -> return m >> verify) moves
but for all x
, we have return x >> a = a
, thus
= map (\_ -> verify) moves
So you discarded the moves. You probably meant to use lift
instead of return
there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With