#parsing #haskell
#синтаксический анализ #haskell
Вопрос:
На языке, который я пишу в данный момент, я пытаюсь реализовать функцию, которая оценивает всю программу на основе того, что я уже написал, поскольку я могу выполнять только один оператор за раз. Функция позволяет мне анализировать и оценивать файлы из файла.
Проблема 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
операторов, а затем выполнить nth
их вручную?
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
Комментарии:
1. Подобно моему комментарию к принятому ответу,
liftM
это древняя реликвия: современный код используетfmap
или(<$>)
. Конечно, это просто вопрос стиля, и если вы уже глубоко привыкли кliftM
нему, вам не повредит придерживаться его (кроме того, что оно применяется реже), но если вы можете выбрать тот, к которому можно привыкнутьfmap
, это более модно.
Ответ №1:
Вы можете использовать mapM
для выполнения шагов IO
, а затем получить список строк:
evalString :: Env -> String -> IO [String]
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
mapM (runIOThrows . liftM show . evalStatement env) x
Это, конечно, дает нам список строк. Если вы хотите обработать этот список после обработки, например, объединить строки, вы можете fmap
это:
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
concat <
gt; mapM (runIOThrows . liftM show . evalStatement env) x
Комментарии:
1. Это идеально! Я предполагаю, что
mapM
это можно использовать во всех случаях, когда требуется сопоставление при работе с типомIO a
, если это имеет какой-то смысл?2. @JiangShi К вашему сведению
traverse
, является более современным эквивалентомmapM
. Если вы собираетесь изучить один из них, я предлагаю использоватьtraverse
, поскольку он более широко применим и делает то же самое.