I got following data type in haskell:
data Flow a = Continue a | Return Value
newtype FlowT m a = FlowT {runFlowT :: m (Flow a)}
type IStateM a = FlowT (StateT IState IO) a
where IState is some record type containing some lists etc. Monad and MonadTrans instances for FlowT are define as follows:
instance (Monad m) => Monad (FlowT m) where
x >>= f = FlowT $ do
unwrapped <- runFlowT x
case unwrapped of
Continue v -> runFlowT (f v)
Return r -> return $ Return r
return x = FlowT $ return (Continue x)
instance MonadTrans FlowT where
lift m = FlowT (Continue `liftM` m)
instance (MonadIO m) => MonadIO (FlowT m) where
liftIO m = lift (liftIO m)
instance (MonadState s m) => MonadState s (FlowT m) where
put k = lift (put k)
get = lift get
My intention was that in the toy language for which I'm developing an interpreter, you can return from function at any moment by calling return with some expression. Now when I'm writing code to interpret invocation of function, I need to extract Flow value being passed behind the scenes in this monad. I cannot pattern match against IState () because it contains IO. The function I need, should work in a similar way that State's get works - I call it and can check if there's some value being returned, and if yes I get it, if no let's say some special value of type Value is returned or something. How to do this?
To pattern match on the value, you must bind its result within the given monad, the exact same way you did within your Monad
instance:
interpret :: FlowT m a -> m a
interpret flow = do
value <- runFlowT flow
case value of
Continue v -> ...
Return r -> ...
As a side note, you've reinvented a special case of free monad transformers, and you can find the official implementation of them in the free
package.
Specifically, your FlowT
type is identical to:
import Control.Monad.Trans.Free -- from the 'free' package
import Data.Functor.Constant -- from the 'transformers' package
type FlowT = FreeT (Constant Value)
This gives an isomorphic type with the exact same behavior and the same Monad
and MonadTrans
instances.
But back to your specific question: No, there is no way to pattern match on the value without binding the result within the base monad first.
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