Parsing 在解析表达式的计算中将[IO字符串]转换为IO字符串

Parsing 在解析表达式的计算中将[IO字符串]转换为IO字符串,parsing,haskell,Parsing,Haskell,在我目前编写的语言中,我试图实现一个函数,该函数根据我已经编写的内容计算整个程序,因为我一次只能执行一条语句。该函数允许我从文件中解析和计算文件 函数evalString是问题所在。例如,如果函数的最后一行是runIOThrows$liftM show$evalStatement env(x!!0),则该函数可以完美执行。我觉得应该采取的自然步骤是使用map,但这只会给我[IO String],而不是IO String 如果我返回函数[IO String],但是readStatement函数和e

在我目前编写的语言中,我试图实现一个函数,该函数根据我已经编写的内容计算整个程序,因为我一次只能执行一条语句。该函数允许我从文件中解析和计算文件

函数
evalString
是问题所在。例如,如果函数的最后一行是
runIOThrows$liftM show$evalStatement env(x!!0)
,则该函数可以完美执行。我觉得应该采取的自然步骤是使用
map
,但这只会给我
[IO String]
,而不是
IO String

如果我返回函数
[IO String]
,但是
readStatement
函数和evalAndPrint函数存在错误:

----- readStatement -----
Couldn't match type ‘IO’ with ‘[]’
      Expected type: [[HStatement]]
        Actual type: IO [HStatement]

----- evalAndPrint -----
Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO ()
        Actual type: [()]

Couldn't match type ‘IO’ with ‘[]’
      Expected type: IO String -> [()]
        Actual type: String -> IO ()
我得到的印象是,使用
map
可以更容易地达到预期效果。如果我按顺序执行每一条语句,那么一切都会完美地工作,那么也许我可以使用
map
来评估
n-1
语句,然后手动执行
n
一条语句

parseProgram :: Parser [HStatement]
parseProgram = spaces *> many (parseEvalHVal <* spaces)

readStatement :: String -> IO [HStatement]
readStatement input = do
         program <- readFile input
         case parse parseProgram "fyp" program of
           Left err -> fail $ show err
           Right parsed -> return $ parsed

evalAndPrint :: Env -> String -> IO ()
evalAndPrint env expr = evalString env expr >>= putStrLn

evalString :: Env -> String -> IO String
evalString env expr = do
         x <- readStatement expr
         putStrLn $ show x
         map (\exprs -> runIOThrows $ liftM show $ evalStatement env exprs) x

run :: String -> IO ()
run expr = nullEnv >>= flip evalAndPrint expr

main :: IO ()
main = do
         args   <- getArgs
         run $ args !! 0

runIOThrows :: IOThrowsError String -> IO String
runIOThrows action = runExceptT (trapError action) >>= return . extractValue


parseProgram::Parser[hs语句]
parseProgram=spaces*>many(ParseEvalVal IO[hsStatement]
readStatement输入=do
程序失败$show err
右解析->返回$parsed
evalAndPrint::Env->String->IO()
evalAndPrint env expr=evalString env expr>>=putStrLn
evalString::Env->String->IO String
evalString env expr=do
x runIOThrows$liftM显示$evalStatement环境表达式)x
运行::字符串->IO()
运行expr=nullEnv>>=flip evalAndPrint expr
main::IO()
main=do
args IO字符串
runIOThrows操作=runExceptT(traperor操作)>>=返回。提取值

您可以使用
mapM
执行
IO
的步骤,然后检索字符串列表:

evalString :: Env -> String -> IO [String]
evalString env expr = do
         x <- readStatement expr
         putStrLn (show x)
         mapM (runIOThrows . liftM show . evalStatement env) x

太好了!我假设
mapM
可以用于处理类型
ioa
时需要map的所有情况,如果这有意义的话?@JiangShi FYI
traverse
mapM
的更现代的等价物。如果你想学习其中一种,我建议你使用
traverse
,因为它的适用范围更广,而且也有同样的作用。与我对公认答案的评论类似,
liftM
是一个古老的遗迹:现代代码使用
fmap
()
。当然,这只是一个风格问题,如果你已经深深习惯了
liftM
,那么坚持使用它不会有什么坏处(除了不太经常使用),但是如果你能选择一个来习惯
fmap
则更时尚。
evalString :: Env -> String -> IO String
evalString env expr = do
         x <- readStatement expr
         putStrLn (show x)
         concat <$> mapM (runIOThrows . liftM show . evalStatement env) x