Reputation: 365
In the language I'm writing at the moment, I'm trying to implement a function which evaluates the entire program based on what I have written already as I can only execute one statement at a time. The function allows me to parse and evaluate files from a file.
The function evalString
is the problem. The function executes perfectly if it's last line were runIOThrows $ liftM show $ evalStatement env (x!!0)
for example. I felt like the natural step to take was to use map
but that just gives me [IO String]
rather than IO String
.
If I make the return of the function [IO String]
however there exists an error with the readStatement
function and evalAndPrint function:
----- 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 ()
I get the impression that there's a much easier way to achieve the desired effect in using map
. If I executed each statement sequentially then everything works perfectly so perhaps I could use map
to evaluate n-1
statements then execute the nth
one manually?
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
Upvotes: 3
Views: 81
Reputation: 477533
You can use mapM
to perform the steps of an IO
and then retrieve the list of strings:
evalString :: Env -> String -> IO [String]
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
mapM (runIOThrows . liftM show . evalStatement env) x
This of course gives us a list of strings. If you want to post-process that list, for example concatenating the strings, you can fmap
it:
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
concat <$> mapM (runIOThrows . liftM show . evalStatement env) x
Upvotes: 4