Following the example file, I try to dynamically call an IO action.
testHint :: Interpreter ()
testHint = do
setImportsQ [("Prelude", Nothing)]
let somecode = "putStrLn \"some string\""
interpret somecode (as :: IO ())
say "hello"
Unfortunately, the string "some string" is never printed. I also tried to call writeFile but no file was created, either.
So what is needed to enable these side effects? Thank you!
interpret won't run any IO actions on its own. It'll just evaluate it and return it to you and then you have to run it yourself, for example using liftIO:
interpret somecode (as :: IO ()) >>= liftIO
Related
I am still a beginner in Haskell, so after reading some writefile tutorials online, I see most of the writefile examples online are used inside the main function (main = IO ())
I am wondering whether it's possible to write a function that writes the results into a file using writefile when it is computed? In some programs (especially games), users might want to stop at a particular points of the game by saving the contents into a .txt file.
For example something like this: (this function does not work, just wondering how to make it work)
concat :: FilePath -> [[a]] -> [a]
concat txt [] = []`
concat txt (xs : xss) = do
y <- xs ++ concat xss
writeFile txt (unlines y)
Thanks:)
The writeFile function has the type FilePath -> String -> IO (), which means that it must run in the IO context.
It doesn't have to run in the main function, but any function that involves IO, including writeFile, will have a return type that involves IO. So you could definitely do something like this:
myFunc :: String -> IO ()
myFunc contents = do
-- do something else
writeFile "foo.txt" contents
-- do more stuff here
You can't, however, call functions that return IO a from pure functions (or, rather, you can't extract the value from the IO container). That's by design; it's how Haskell works, and it's a good thing. If you want to enable users to perform impure actions at arbitrary times, you must design for such a feature. Here's my introduction to pure interactions - that's one place to start.
Yes, you can use writeFile in other places than main, but for a place to qualify, the type IO has to be a part of that place's type signature. (The reason I'm saying place is because main isn't a function, but your concat is a function. And the place you want to look at putting your writeFile call has to be an IO action, which can be the result of a function or not.)
You mentioned saving something related to a game into a .txt file. An example of that could be:
saveGame :: FilePath -> GameState -> IO ()
saveGame gameFile gameState =
writeFile gameFile (serializeGame gameState)
serializeGame :: GameState -> String
serializeGame (GameState ...) = ...
runGame :: GameState -> IO ()
runGame gameState = do
...
if wantsToSaveGame
then saveGame gameFile gameState
else ...
...
runGame updatedGameState
main :: IO ()
main = do
...
runGame initialGameState
In this contrived example, serializeGame would not be a suitable place to call saveGame because it's a pure function, whereas runGame is a self-recursive IO () action capable of affecting files on your file system.
An example of a related IO action that isn't a function could be this one:
resetSaveGame :: IO ()
resetSaveGame =
saveGame defaultGameFile initialGameState
In Haskell, the type constructor IO is a monad equipped with a return statement that lifts any expression to its IO version.
Nothing prevents us from lifting what is already an IO action to its IO version - giving us a type of the form IO (IO a).
So I can for example write the following program:
main = return . print $ "Hello world"
Which upon execution does exactly nothing.
My question is, what happens under the hood when this main is executed?
Are there any situations where it makes sense to return an IO action?
Under the hood, the runtime effectively discards the result of the IO action main, which is why it is usually defined as IO (). This means that if main actually has a type like IO (IO Int), there is no real issue. The IO action is executed, and the result (another IO action) is discarded, unexecuted.
Deeper in your program, you are more likely to just trigger a type error. For example, fmap doSomething (return . getLine) won't type-check if you meant fmap doSomething getLine.
IO is often approximated to State RealWorld, that is a State monad that operates on the "real world". return for State produces an action that does not alter contained state. So, by analogy, returning anything to IO doesn't do anything as well. Not only when you return some IO a, but any a.
Returning an IO action may be used to build up a computation in one place (capturing some values into a closure along the way) and execute it somewhere else. Much like passing a callback in C.
Actually, to build up an IO computation and pass it somewhere else you can use a let inside of a do-block:
main :: IO ()
main = do
let act = readFile "somefile" -- action is not performed yet
foo act -- pass the action as a parameter
foo :: IO () -> IO ()
foo act = do
.. do something
result <- act -- actually perform the action
..
In this case you don't need to return an IO a value.
But if the process of building that computation itself requires you to perform IO actions, that's where you'd need such a thing. In our example, let's ask user of filename to be opened:
main :: IO ()
main = do
act <- do
filename <- getLine
return (readFile filename)
foo act
Here, we need a filename to create our action, but getLine is in IO as well. That's where additional level of IO comes out. Of course, this example is synthetic, as you can just do filename <- getLine right in main.
How come I can write something like this:
main :: IO ()
main = getLine >>= putStrLn
Since getLine :: IO String and putStrLn :: String -> IO String it would seem that the overall type of this action should be IO String. Why does this compile instead of giving a type error? The only reason I can come up with is that at compile time >> return () is added to the end of the value of main. So what's going on here?
Actually, putStrLn :: String -> IO (). So there's nothing magical happening at all.
You can also verify this by writing
main :: IO String
main = getLine
and observing that this both (1) does compile and (2) couldn't possibly compile if there were an implicit >> return () inserted into main.
I have code in the Reader Monad, so as to pass a file handle as an invisible parameter down the Reader chain.
In writeMail, I am trying to create a Reader, which, when run using runReader, produces an IO () output which is itself the result of a chain of IO monads
writeMail :: Reader Handle (IO ())
writeMail mail = do
wmh <- writeMailHeaders mail
wmb <- writeMailBody mail
return $ wmh >>= \_ -> wmb
However I am finding that only the last in the IO chain i.e. wmb, prints at the console.
Can anyone see what I should be doing to get wmh, then wmb to print?
With simpler example:
module Read where
import Data.Functor.Identity
write :: Monad m => m (IO ())
write = do
a <- return $ putStrLn "foo"
b <- return $ putStrLn "bar"
return $ a >> b
main :: IO ()
main = runIdentity write
main prints both "foo" and "bar". So I suspect the error is in writeMailHeaders.
What you need is not just a reader, but a ReaderT monad transformer with IO as a base monad.
Since your example was incomplete, I made some changes to show your options:
import Control.Monad.Reader
writeMail :: ReaderT Handle IO ()
writeMail = do
-- Here's how you get your handle to further do something to it:
handle <- ask
-- Here's how you do the IO actions.
-- Notice the `lift` function,
-- which allows us to run actions of the base monad,
-- which in that case is `IO`.
lift $ do
print "bla bla"
print "bla"
Most of this is straight from the hint example. What I'd like to do is initialize the interpreter with modules and imports and such and keep it around somehow. Later on (user events, or whatever), I want to be able to call a function with that initialized state and interpret an expression many times. So at the --split here location in the code, I want to have the code above in init, and the code below that in a new function that takes an expression and interprets it.
module Main where
import Language.Haskell.Interpreter
import Test.SomeModule
main :: IO ()
main = do r <- runInterpreter testHint
case r of
Left err -> printInterpreterError err
Right () -> putStrLn "Done."
-- Right here I want to do something like the following
-- but how do I do testInterpret thing so it uses the
-- pre-initialized interpreter?
case (testInterpret "expression one")
Left err -> printInterpreterError err
Right () -> putStrLn "Done."
case (testInterpret "expression two")
Left err -> printInterpreterError err
Right () -> putStrLn "Done."
testHint :: Interpreter ()
testHint =
do
loadModules ["src/Test/SomeModule.hs"]
setImportsQ [("Prelude", Nothing), ("Test.SomeModule", Just "SM")]
say "loaded"
-- Split here, so what I want is something like this though I know
-- this doesn't make sense as is:
-- testExpr = Interpreter () -> String -> Interpreter ()
-- testExpr hintmonad expr = interpret expr
let expr1 = "let p1o1 = SM.exported undefined; p1o2 = SM.exported undefined; in p1o1"
say $ "e.g. typeOf " ++ expr1
say =<< typeOf expr1
say :: String -> Interpreter ()
say = liftIO . putStrLn
printInterpreterError :: InterpreterError -> IO ()
printInterpreterError e = putStrLn $ "Ups... " ++ (show e)
I'm having trouble understanding your question. Also I am not very familiar with hint. But I'll give it a go.
As far as I can tell, the Interpreter monad is just a simple state wrapper around IO -- it only exists so that you can say eg. setImportsQ [...] and have subsequent computations depend on the "settings" that were modified by that function. So basically you want to share the monadic context of multiple computations. The only way to do that is by staying within the monad -- by building a single computation in Interpreter and running it once. You can't have a "global variable" that escapes and reuses runInterpreter.
Fortunately, Interpreter is an instance of MonadIO, which means you can interleave IO computations and Interpreter computations using liftIO :: IO a -> Interpreter a. Basically you are thinking inside-out (an extremely common mistake for learners of Haskell). Instead of using a function in IO that runs code in your interpreter, use a function in Interpreter that runs code in IO (namely liftIO). So eg.
main = runInterpreter $ do
testHint
expr1 <- liftIO getLine
r1 <- interpret "" expr1
case r1 of
...
expr2 <- liftIO getLine
r2 <- interpret "" expr2
case r2 of
...
And you can easily pull that latter code out into a function if you need to, using the beauty of referential transparency! Just pull it straight out.
runSession :: Interpreter ()
runSession = do
expr1 <- liftIO getLine
r1 <- interpret "" expr1
case interpret expr1 of
...
main = runInterpreter $ do
testHint
runSession
Does that make sense? Your whole program is an Interpreter computation, and only at the last minute do you pull it out into IO.
(That does not mean that every function you write should be in the Interpreter monad. Far from it! As usual, use Interpreter around the edges of your program and keep the core purely functional. Interpreter is the new IO).
If I understand correctly, you want to initialize the compiler once, and run multiple queries, possibly interactively.
There are two main approaches:
lift IO actions into your Interpreter context (see luqui's answer).
use lazy IO to smuggle a stream of data in and out of your program.
I'll describe the second option.
By the magic of lazy IO, you can pass testHint a lazy stream of input, then loop in the body of testHint, interpreting many queries interactively:
main = do
ls <- getContents -- a stream of future input
r <- runInterpreter (testHint (lines input))
case r of
Left err -> printInterpreterError err
Right () -> putStrLn "Done."
testHint input = do
loadModules ["src/Test/SomeModule.hs"]
setImportsQ [("Prelude", Nothing), ("Test.SomeModule", Just "SM")]
say "loaded"
-- loop over the stream of input, interpreting commands
let go (":quit":es) = return ()
(e:es) = do say =<< typeOf e
go es
go
The go function has access to the closed-over environment of the initialized interpreter, so feeding it events will obviously run in the scope of that once-initialized interpreter.
An alternative method would be to extract the interpreter state from the monad, but I'm not sure that is possible in GHC (it would require GHC not to be in the IO monad fundamentally).