将monadic函数转换为IO monadic函数

时间:2011-10-11 15:29:01

标签: haskell

parseSource :: String -> Either ParserError Mod.Module
parseSource src = do
    (imports, rest) <- parseImports (Lex.lexSource src)
    bindings <- mapM parseBinding rest
    buildModule imports bindings

我需要使上面的返回IO (Either ParserError Mod.Module),因为最后buildModule语句需要执行一些IO功能(读取文件)。我遇到的问题是,当我使它成为IO函数时,我不能再进行绑定(错误的术语?)<-操作。

使这项工作最简单的方法是什么?

2 个答案:

答案 0 :(得分:6)

看看根据ErrorT ParseError IO定义问题。

答案 1 :(得分:3)

我找不到组合器将纯Either计算提升到ErrorT monad,所以我写了一个名为liftError的组合。我用虚拟类型和实现充实了你的例子。 main运行解析器两次,一次输入引发ParserError,一次运行IO副作用。要使ErrorT ParserError IO成为MonadParserError必须是Error的实例(以便可以实现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"]