Converting a monadic function to an IO monadic function
parseSource :: String -> Either ParserError Mod.Module
parseSource src = do
(imports, rest) <- parseImports (Lex.lexSource src)
bindings <- mapM parseBinding rest
buildModule imports bindings
I need to make the above return an IO (Either ParserError Mod.Module)
as the buildModule
statement at the end will need to perform some IO functions (reading files). The problem i have is that when i make i开发者_StackOverflow社区t an IO function, i can no longer do the bind(wrong term?) <-
operations.
What is the simplest way to make this work?
Take a look at defining your problem in terms of ErrorT ParseError IO.
I couldn't find a combinator to lift a pure Either
computation into the ErrorT
monad, so I wrote one called liftError
. I fleshed out your example with dummy types and implementations. The main
runs the parser twice, once with input that throws a ParserError
, and once which succeeds with an IO
side-effect. In order for ErrorT ParserError IO
to be a Monad
, ParserError
must be an instance of Error
(so that it is possible to implement fail
).
import Control.Monad.Error
type ParserMonad = ErrorT ParserError IO
data ParserError = ParserError1 | ParserError2 | ParserError3
deriving(Show)
data Module = Module
deriving(Show)
data Import = Import
deriving(Show)
data Binding = Binding
deriving(Show)
instance Error ParserError where
noMsg = undefined
-- lift a pure Either into the ErrorT monad
liftError :: Monad m => Either e a -> ErrorT e m a
liftError = ErrorT . return
parseSource :: String -> ParserMonad Module
parseSource src = do
(imports, rest) <- liftError $ parseImports (lexSource src)
bindings <- liftError $ mapM parseBinding rest
buildModule imports bindings
lexSource :: String -> [String]
lexSource = return
parseImports :: [String] -> Either ParserError ([Import], [String])
parseImports toks = do{ when (null toks) $ throwError ParserError1
; return ([Import], toks)
}
parseBinding :: String -> Either ParserError Binding
parseBinding b = do{ when (b == "hello") $ throwError ParserError2
; return Binding
}
buildModule :: [Import] -> [Binding] -> ParserMonad Module
buildModule i b = do{ liftIO $ print "hello"
; when (null b) $ throwError ParserError3
; return Module
}
main = mapM (runErrorT . parseSource) ["hello", "world"]
精彩评论