Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern match against value in monad

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?

like image 957
k_wisniewski Avatar asked May 06 '13 19:05

k_wisniewski


Video Answer


1 Answers

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.

like image 152
Gabriella Gonzalez Avatar answered Nov 05 '22 20:11

Gabriella Gonzalez