Mr.Bloom
Mr.Bloom

Reputation: 365

Converting [IO String] to IO String in evaluation of parsed expression

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

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

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

Related Questions