I've recently been experimenting with a little "make" engine I've implemented in Haskell. I'm using state monads to do the work. I was attempting to use two functions: matchRuleST :: String -> State RuleSet (Maybe Rule) makeST :: String -> StateT RuleSet IO () matchRuleST doesn't really need IO for anything (it just works on the current state "RuleSet"). makeST needs IO (it does file date comparisons, actions on the files, etc... the usual "make" stuff). The problem is how to properly use matchRuleST from makeST. I searched for a proper lifting function (similar to liftIO, but which would lift from the state monad), and couldn't find one. So I changed the function signature to: matchRuleST :: String -> StateT RuleSet IO (Maybe Rule) and everything worked just fine. Then, I decided to try again, and came up with this function: liftState :: State s a -> StateT s m a liftState s = do state1 <- get ( let (result, state) = evalState (do {result <- s; state <- get; return (result, state)}) state1 in do put state return result ) which works like a charm and is not even IO-dependent. I'm learning here, so the questions are: Is there a better way to do this? Did I miss a similar function anywhere? Did I miss some useful abstraction that makes this a moot point? I'm using GHC 6.4. JCAB
[I think it is preferred to post this on haskell-cafe.] On Fri, Mar 18, 2005 at 02:00:37PM -0800, Juan Carlos Arevalo Baeza wrote:
matchRuleST :: String -> State RuleSet (Maybe Rule) makeST :: String -> StateT RuleSet IO ()
matchRuleST doesn't really need IO for anything (it just works on the current state "RuleSet"). makeST needs IO (it does file date comparisons, actions on the files, etc... the usual "make" stuff). The problem is how to properly use matchRuleST from makeST.
You might solve this by changing the type of matchRuleST: matchRuleST :: MonadState RuleSet m => String -> m (maybe Rule) (This requires -fglasgow-exts, but I don't think there's anything controversial about non-variables in class constraints.)
Then, I decided to try again, and came up with this function:
liftState :: State s a -> StateT s m a
(I think you left out the constraint (Monad m).)
liftState s = do state1 <- get ( let (result, state) = evalState (do {result <- s; state <- get; return (result, state)}) state1 in do put state return result )
You can turn this into a one-liner if you work on it a bit. But I would go with the above. Aside: It bugs me that this is not defined by Control.Monad.State (alongside modify and gets): state :: MonadState s m => (s -> (a, s)) -> m a I almost always end up defining it myself and use it to implement other state transformers. I would do the same for other monad classes (Writer, etc): provide a function that captures the general operation. Andrew
participants (2)
-
Andrew Pimlott -
Juan Carlos Arevalo Baeza