GHC as a library - getting output from GHCI

Hi I am trying to use GHC as a library (see http://haskell.org/haskellwiki/GHC/As_a_library ). I want to get all the output from an interactive session and put in a GUI. This http://haskell.org/sitewiki/images/5/51/Interactive.hs seemed to be a nice starting point. However, the result of: GHC.runStmt session stmt does not include the output of evaluating the statement (stmt above) - just the names of bound names. That is if stmt eqauls "3 + 5" then GHC.runStmt do not return a string equaling "8", but just the "it"-name (which is bound to 8). The output is printed to standard output :( And I do not see and easy way to get hold of it. The only way I see, is having the GUI in one process and "GHC as a library" in another process. Do anybody know of an easy way to get hold of the output from "GHC as a library" - the output currently printed to standard output? Greetings, Mads Lindstrøm

I believe that the trick is to wrap your stmt in a IO handler that captures stdout and returns it together with the value of your stmt. That is, something with a type: wrapStmt :: IO a -> IO (a,String) It should be easy to implement wrapStmt using System.Posix.Process. Then you just define runStmt' session stmt = runStmt session ("wrapStmt $ " ++ stmt) Good Luck! On 04/05/2007, at 18:04, Mads Lindstrøm wrote:
Hi
I am trying to use GHC as a library (see http://haskell.org/haskellwiki/GHC/As_a_library ). I want to get all the output from an interactive session and put in a GUI. This http://haskell.org/sitewiki/images/5/51/Interactive.hs seemed to be a nice starting point.
However, the result of:
GHC.runStmt session stmt
does not include the output of evaluating the statement (stmt above) - just the names of bound names. That is if stmt eqauls "3 + 5" then GHC.runStmt do not return a string equaling "8", but just the "it"- name (which is bound to 8).
The output is printed to standard output :( And I do not see and easy way to get hold of it. The only way I see, is having the GUI in one process and "GHC as a library" in another process.
Do anybody know of an easy way to get hold of the output from "GHC as a library" - the output currently printed to standard output?
Greetings,
Mads Lindstrøm
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Hi Pepe I would have liked something cross-platform. Also, if stmt contains an error, wrapStmt will not be evaluated, and the resulting error is written to standard output or maybe to standard error. Whichever way, I am back to the same problem. Greetings, Mads Lindstrøm
I believe that the trick is to wrap your stmt in a IO handler that captures stdout and returns it together with the value of your stmt. That is, something with a type:
wrapStmt :: IO a -> IO (a,String)
It should be easy to implement wrapStmt using System.Posix.Process. Then you just define
runStmt' session stmt = runStmt session ("wrapStmt $ " ++ stmt)
Good Luck!
On 04/05/2007, at 18:04, Mads Lindstrøm wrote:
Hi
I am trying to use GHC as a library (see http://haskell.org/haskellwiki/GHC/As_a_library ). I want to get all the output from an interactive session and put in a GUI. This http://haskell.org/sitewiki/images/5/51/Interactive.hs seemed to be a nice starting point.
However, the result of:
GHC.runStmt session stmt
does not include the output of evaluating the statement (stmt above) - just the names of bound names. That is if stmt eqauls "3 + 5" then GHC.runStmt do not return a string equaling "8", but just the "it"- name (which is bound to 8).
The output is printed to standard output :( And I do not see and easy way to get hold of it. The only way I see, is having the GUI in one process and "GHC as a library" in another process.
Do anybody know of an easy way to get hold of the output from "GHC as a library" - the output currently printed to standard output?
Greetings,
Mads Lindstrøm
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Mads On 04/05/2007, at 19:19, Mads Lindstrøm wrote:
Hi Pepe
I would have liked something cross-platform.
Take a look at the unix-compat[1] package by Bjorn Bringert, although it looks like it won't help you. Maybe it can be extended.
Also, if stmt contains an error, wrapStmt will not be evaluated, and the resulting error is written to standard output or maybe to standard error. Whichever way, I am back to the same problem.
What else should be the behaviour in the case stmt contains an error? You can capture errors and stop them being written out very easily. Take a look at this snippet (not mine, it's beschmi's code) from Shim [2]: \begin{code} load' :: FilePath -> Maybe String -> SHM (SuccessFlag, [CompileNote],Session) load' sourcefile source = do source' <- addTime source ses <- getSessionFor sourcefile dflags0 <- io $ GHC.getSessionDynFlags ses ref <- io $ MVar.newMVar [] let dflags1 = dflags0{ log_action = logMsg ref } io $ GHC.setSessionDynFlags ses dflags1 io $ GHC.setTargets ses [Target (TargetFile sourcefile Nothing) source'] loadResult <- io $ GHC.load ses LoadAllTargets cnotes <- io $ reverse `liftM` MVar.readMVar ref case loadResult of Succeeded -> do -- GHC takes care of setting the right context modq <- io $ findModuleInFile ses sourcefile io $ GHC.setContext ses [modq] [] return (Succeeded,cnotes,ses) Failed -> do -- We take care of getting at least the Prelude io(GHC.setContext ses [] =<< atomM (getPrelude ses)) return (Failed,cnotes,ses) where atomM = liftM (:[]) logMsg ref severity' srcSpan' style' msg' = do dir <- getCurrentDirectory logS ('\n':show ((mkLocMessage srcSpan' msg') style')) MVar.modifyMVar_ ref (\l -> return $ (CompileNote severity' srcSpan' style' msg' dir):l) \end{code} Here, DynFlags.logAction is being filled with a handler that writes to a MVar instead of the default handler that writes to stdout. pepe [1] - http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix- compat-0.1 [2] - http://shim.haskellco.de/shim/Shim/Hsinfo.hs

Hi Pepe Pepe Iborra wrote:
Mads
On 04/05/2007, at 19:19, Mads Lindstrøm wrote:
Hi Pepe
I would have liked something cross-platform.
Take a look at the unix-compat[1] package by Bjorn Bringert, although it looks like it won't help you. Maybe it can be extended.
Also, if stmt contains an error, wrapStmt will not be evaluated, and the
resulting error is written to standard output or maybe to standard
error. Whichever way, I am back to the same problem.
What else should be the behaviour in the case stmt contains an error?
It could behave like most other IO actions which produce values - return them to the caller. For instance readFile :: FilePath -> IO String . runStmt could have the following type: runStmt :: Session -> String -> IO (RunResult, String) where the returned String would contain either the error or the result from evaluating the statement.
You can capture errors and stop them being written out very easily. Take a look at this snippet (not mine, it's beschmi's code) from Shim[2]:
Yes, that works for me. Greetings, Mads
\begin{code} load' :: FilePath -> Maybe String -> SHM (SuccessFlag,[CompileNote],Session) load' sourcefile source = do source' <- addTime source ses <- getSessionFor sourcefile dflags0 <- io $ GHC.getSessionDynFlags ses ref <- io $ MVar.newMVar [] let dflags1 = dflags0{ log_action = logMsg ref } io $ GHC.setSessionDynFlags ses dflags1 io $ GHC.setTargets ses [Target (TargetFile sourcefile Nothing) source'] loadResult <- io $ GHC.load ses LoadAllTargets cnotes <- io $ reverse `liftM` MVar.readMVar ref case loadResult of Succeeded -> do -- GHC takes care of setting the right context modq <- io $ findModuleInFile ses sourcefile io $ GHC.setContext ses [modq] [] return (Succeeded,cnotes,ses) Failed -> do -- We take care of getting at least the Prelude io(GHC.setContext ses [] =<< atomM (getPrelude ses)) return (Failed,cnotes,ses) where atomM = liftM (:[]) logMsg ref severity' srcSpan' style' msg' = do dir <- getCurrentDirectory logS ('\n':show ((mkLocMessage srcSpan' msg') style')) MVar.modifyMVar_ ref (\l -> return $ (CompileNote severity' srcSpan' style' msg' dir):l) \end{code}
Here, DynFlags.logAction is being filled with a handler that writes to a MVar instead of the default handler that writes to stdout.
pepe
[1] - http://hackage.haskell.org/cgi-bin/hackage -scripts/package/unix-compat-0.1 [2] - http://shim.haskellco.de/shim/Shim/Hsinfo.hs

On Fri, May 04, 2007 at 08:12:55PM +0200, Pepe Iborra wrote:
Mads
On 04/05/2007, at 19:19, Mads Lindstr?m wrote:
Hi Pepe
I would have liked something cross-platform.
Take a look at the unix-compat[1] package by Bjorn Bringert, although it looks like it won't help you. Maybe it can be extended.
I'm attempting to do the same thing. But I don't see how wrapStmt can be implemented with System.Posix.Process. Documentation doesn't seem to indicate any function that deals with Handles, or returns Strings. I am unfamiliar with that module however. -- -- Matthew Danish -- user: mrd domain: cmu.edu -- OpenPGP public key: C24B6010 on keyring.debian.org

Hi Look at System.Posix.IO I do not know if that module can do what you want. But it does deal with FileDescriptors and handles. Maybe the dup function can help you. According to http://www2.lib.uchicago.edu/~keith//tcl-course/topics/processes.html it does: "The dup implements the dup system call, which duplicates one desired open file descriptor into another. This can be used connect standard input or standard output to a pipe. This sample code shows how a parent process can fork the standard Unix sort command and then feed it data to be sorted. A simple extension would allow the child to write the results back to the parent." But I have not tested it yet, and I am not really familiar with Unix inter-process communication. Greetings, Mads Matthew Danish wrote:
On Fri, May 04, 2007 at 08:12:55PM +0200, Pepe Iborra wrote:
Mads
On 04/05/2007, at 19:19, Mads Lindstr?m wrote:
Hi Pepe
I would have liked something cross-platform.
Take a look at the unix-compat[1] package by Bjorn Bringert, although it looks like it won't help you. Maybe it can be extended.
I'm attempting to do the same thing. But I don't see how wrapStmt can be implemented with System.Posix.Process. Documentation doesn't seem to indicate any function that deals with Handles, or returns Strings. I am unfamiliar with that module however.

On Wed, May 09, 2007 at 10:48:15PM +0200, Mads Lindstr?m wrote:
Hi
Look at System.Posix.IO
I do not know if that module can do what you want. But it does deal with FileDescriptors and handles.
Maybe the dup function can help you. According to http://www2.lib.uchicago.edu/~keith//tcl-course/topics/processes.html it does:
"The dup implements the dup system call, which duplicates one desired open file descriptor into another. This can be used connect standard input or standard output to a pipe. This sample code shows how a parent process can fork the standard Unix sort command and then feed it data to be sorted. A simple extension would allow the child to write the results back to the parent."
But I have not tested it yet, and I am not really familiar with Unix inter-process communication.
Right, good thinking. This is what I've come up with for Unix-only:
module WrapIO where import System.IO import System.Posix.IO import Control.Exception
wrapIO :: IO a -> IO (a, String) wrapIO action = do oldStdOutput <- dup stdOutput (rd, wr) <- createPipe dupTo wr stdOutput v <- action h <- fdToHandle rd closeFd wr s <- hGetContents h return (v, s) `finally` do dupTo oldStdOutput stdOutput closeFd oldStdOutput
Unfortunately, dupTo will not work on Win32 afaik. -- -- Matthew Danish -- user: mrd domain: cmu.edu -- OpenPGP public key: C24B6010 on keyring.debian.org

Hi While wrapIO works in most cases, try running this: :m +Control.Concurrent forkIO (let foo = do threadDelay 1000000; print "A"; foo in foo) I am not saying that this makes wrapIO unusable - just that it is not bulletproof. If not using forkIO is ok, then this is a much easier solution, than creating a separate process for running "GHC as a library" - that is having the GUI in one process and having "GHC as a library in a child process. Greetings, Mads Matthew Danish wrote:
On Wed, May 09, 2007 at 10:48:15PM +0200, Mads Lindstr?m wrote:
Hi
Look at System.Posix.IO
I do not know if that module can do what you want. But it does deal with FileDescriptors and handles.
Maybe the dup function can help you. According to http://www2.lib.uchicago.edu/~keith//tcl-course/topics/processes.html it does:
"The dup implements the dup system call, which duplicates one desired open file descriptor into another. This can be used connect standard input or standard output to a pipe. This sample code shows how a parent process can fork the standard Unix sort command and then feed it data to be sorted. A simple extension would allow the child to write the results back to the parent."
But I have not tested it yet, and I am not really familiar with Unix inter-process communication.
Right, good thinking. This is what I've come up with for Unix-only:
module WrapIO where import System.IO import System.Posix.IO import Control.Exception
wrapIO :: IO a -> IO (a, String) wrapIO action = do oldStdOutput <- dup stdOutput (rd, wr) <- createPipe dupTo wr stdOutput v <- action h <- fdToHandle rd closeFd wr s <- hGetContents h return (v, s) `finally` do dupTo oldStdOutput stdOutput closeFd oldStdOutput
Unfortunately, dupTo will not work on Win32 afaik.

| I am trying to use GHC as a library (see | http://haskell.org/haskellwiki/GHC/As_a_library ). I want to get all the | output from an interactive session and put in a GUI. This | http://haskell.org/sitewiki/images/5/51/Interactive.hs seemed to be a | nice starting point. If you want to collect *all* the output, that presumably includes the output of printing to 'stdout'? That is, if I type putStr "Hello" I see the result. If you manage to redirect stdout in this way, I think you will also see the value of your 'stmt', because what GHC does is to compile a little (print it) stmt and run it. It's a while since I looked at the Interactive loop, but I think that's right. Simon

Hi Simon The Interactive.hs program do not really redirect stdout. It intercepts calls to putStrLn and getLine via let definitions: mustWork "let putStrLn = MyPrelude.myPutStrLn" mustWork "let getLine = MyPrelude.myGetLine" -- mustWork either runs the given statement successfully or triggers an error where mustWork stmt = do result <- GHC.runStmt session stmt if isOk result then return () else error "replaceFunctions failed." Thus, if GHCi's print loop is not within the context of the let definitions, it will not use myPutStrLn and myGetLine. If I run the Interactive.hs program on my Debian/Linux box, and write "3+4" then its printed directly to standard output. It does not use myPutStrLn function. Also if stmt = SomeModule.prettyPrinter "foobar" and SomeModule contains prettyPrinter x = putStrLn $ "Pretty: " ++ x then the let binding will not catch it. Thus we have two problems. One is output from evaluating expressions (like "3+2"), and another is output from a user module not affected by the let-binding above. Therefore a better solution is to run "GHC as a library" in a separate process, from which we read/write stdin, stdout, and stderr. I am exploring several different options for running processes in Haskell, but the only useful (for this particular purpose) seems to be the Unix-only System.Posix.Process. I could also use System.Process.runInteractiveCommand, which gives me handles for stdin, stdout, stderr. But it does not seem possible to add additional channels. Therefore control information (like getModuleGraph, runStmt, or getBindings) must also go though stdin/stdout. It seems very problematic to have control information, send though the same channel as the input/output from running runStmt. For one thing the output from runStmt can contain arbitrary data, and could therefore resemble control information. Secondly, I could use Control.Concurrent. But this do not allow me to redirect stdout, stderr or stdin. Greetings, Mads Simon Peyton-Jones wrote:
| I am trying to use GHC as a library (see | http://haskell.org/haskellwiki/GHC/As_a_library ). I want to get all the | output from an interactive session and put in a GUI. This | http://haskell.org/sitewiki/images/5/51/Interactive.hs seemed to be a | nice starting point.
If you want to collect *all* the output, that presumably includes the output of printing to 'stdout'? That is, if I type putStr "Hello" I see the result.
If you manage to redirect stdout in this way, I think you will also see the value of your 'stmt', because what GHC does is to compile a little (print it) stmt and run it.
It's a while since I looked at the Interactive loop, but I think that's right.
Simon

| Also if stmt = | | SomeModule.prettyPrinter "foobar" | | and SomeModule contains | | prettyPrinter x = putStrLn $ "Pretty: " ++ x | | then the let binding will not catch it. Indeed, that's exactly what I meant in my original msg. I have always thought it ugly that stdin and stdout are top-level definitions. I want to be able to say withStdout :: Handle -> IO a -> IO a so that (withStdout h a) runs 'a' but sends all std-out output to h. Haskell should support this. Starting a whole OS process to redirect stdout must surely be overkill! (But maybe that is what you have to do today.) S

Simon Peyton-Jones wrote:
| Also if stmt = | | SomeModule.prettyPrinter "foobar" | | and SomeModule contains | | prettyPrinter x = putStrLn $ "Pretty: " ++ x | | then the let binding will not catch it.
Indeed, that's exactly what I meant in my original msg. I have always thought it ugly that stdin and stdout are top-level definitions. I want to be able to say
withStdout :: Handle -> IO a -> IO a
so that (withStdout h a) runs 'a' but sends all std-out output to h.
Haskell should support this. Starting a whole OS process to redirect stdout must surely be overkill!
And we do have support for this in GHC. $ ghci ___ ___ _ / _ \ /\ /\/ __(_) / /_\// /_/ / / | | GHC Interactive, version 6.7, for Haskell 98. / /_\\/ __ / /___| | http://www.haskell.org/ghc/ \____/\/ /_/\____/|_| Type :? for help. Loading package base ... linking ... done. Prelude> :m + System.IO Prelude System.IO> h <- openFile "out" WriteMode {handle: out} Prelude System.IO> GHC.Handle.hDuplicateTo h stdout -- as this point, GHCi goes quiet: I type ":quit" $ cat out Prelude System.IO> :quit Leaving GHCi. Cheers, Simon

On Thu, May 10, 2007 at 09:34:53AM +0100, Simon Marlow wrote:
And we do have support for this in GHC.
$ ghci ___ ___ _ / _ \ /\ /\/ __(_) / /_\// /_/ / / | | GHC Interactive, version 6.7, for Haskell 98. / /_\\/ __ / /___| | http://www.haskell.org/ghc/ \____/\/ /_/\____/|_| Type :? for help.
Loading package base ... linking ... done. Prelude> :m + System.IO Prelude System.IO> h <- openFile "out" WriteMode {handle: out} Prelude System.IO> GHC.Handle.hDuplicateTo h stdout -- as this point, GHCi goes quiet: I type ":quit" $ cat out Prelude System.IO> :quit Leaving GHCi.
Cheers, Simon
Is there any way to create a handle which dumps its input into a string or memory buffer of some kind? I only know of the ability to open file/socket handles, and a quick browse of GHC/Handle.hs doesn't seem to indicate anything else. I'm not counting createPipe/fdToHandle because that is not portable. -- -- Matthew Danish -- user: mrd domain: cmu.edu -- OpenPGP public key: C24B6010 on keyring.debian.org

Matthew Danish wrote:
On Thu, May 10, 2007 at 09:34:53AM +0100, Simon Marlow wrote:
And we do have support for this in GHC.
$ ghci ___ ___ _ / _ \ /\ /\/ __(_) / /_\// /_/ / / | | GHC Interactive, version 6.7, for Haskell 98. / /_\\/ __ / /___| | http://www.haskell.org/ghc/ \____/\/ /_/\____/|_| Type :? for help.
Loading package base ... linking ... done. Prelude> :m + System.IO Prelude System.IO> h <- openFile "out" WriteMode {handle: out} Prelude System.IO> GHC.Handle.hDuplicateTo h stdout -- as this point, GHCi goes quiet: I type ":quit" $ cat out Prelude System.IO> :quit Leaving GHCi.
Cheers, Simon
Is there any way to create a handle which dumps its input into a string or memory buffer of some kind? I only know of the ability to open file/socket handles, and a quick browse of GHC/Handle.hs doesn't seem to indicate anything else. I'm not counting createPipe/fdToHandle because that is not portable.
Sadly not at the moment. Our Handle implementation is currently always backed by a file descriptor. It would certainly be possible (though not easy) to extend it in this direction. Cheers, Simon
participants (5)
-
Mads Lindstrøm
-
Matthew Danish
-
Pepe Iborra
-
Simon Marlow
-
Simon Peyton-Jones