Goal
I'm trying to write the internals of an interpreter, and, for ergonomics purposes, I think I want to have a monad that works like both the state and either monads.
For example, I'd like to do some things with either style:
checkedAddress :: Integer -> Interpreter Int
checkedAddress n = if (n < toInteger (minBound :: Int))
then fail $ "Address " ++ show n ++ " is too low"
else if (n > toInteger (maxBound :: Int))
then fail $ "Address " ++ show n ++ " is too high"
else return $ fromInteger n
I'd like to do other things with state style:
setInstructionPointer :: Int -> Interpreter ()
setInstructionPointer ip (Machine _ mem) = ((), Machine ip mem)
getInstructionPointer :: Interpreter Int
getInstructionPointer m@(Machine ip mem) = (ip, m)
Questions
Is it possible to create a state-either hybrid monad like this?
If it's impossible, why is it impossible? Is there an alternative that has the nice ergonomics and, I assume, efficiency of early termination of (like either stops further processing through Left m >>= _ = Left m
) this approach?
If it's possible, how do I write the monad instance for the type? I tried, but I got stuck when writing (>>=)
because I can't see a way to know what constructor to produce without knowing the runtime Machine
value.
data Interpreter a = Running (Machine -> (a, Machine))
| Halted (Machine -> Machine)
| Error String (Machine -> Machine)
instance Monad Interpreter where
return = Running . (,)
Running f >>= g = DontKnowWhich $ \ m -> let (a, m') = f m
in case g a of
Running h ->
Halted h ->
Error s h ->
h@(Halted _) >>= _ = h
e@(Error _ _) >>= _ = e