
You might consider that ghci is just a client of ghc-api that's built into
ghc for convenience; see the ghci-ng package on hackage, which is a
standalone version used to experiment with new ghci features. ghci itself
has a separate interpreter, in other words. (Note also
-fexternal-interpreter, which relies on this to move the backend to a
different process or even different host for cross-platform work; you might
want to look into customization at that level.)
On Sun, Mar 31, 2019 at 6:51 PM Mario Lang
Hi.
Since I discovered getExternalPrint, I found a ton of use cases for it. In particular, it makes it possible to write background threads that report stuff to the console without disturbing the prompt.
I ended up writing a sort of uGHCi with the help of hint and the unreleased master branch of haskeline[1] to make externalPrint available from within the interpreter. Combined with Shh, this opens the door for a lot of useful functionality. Here is a simplified example based on shell programming with the help of Shh:
% let watch r p = forkIO . forever $ printProc p >> readIORef r >>= OS.sleep % delay <- newIORef 10 % clock <- watch delay OS.date Sat Mar 2 21:32:28 CET 2019 Sat Mar 2 21:32:38 CET 2019 % writeIORef delay 5 Sat Mar 2 21:32:48 CET 2019 Sat Mar 2 21:32:53 CET 2019 Sat Mar 2 21:32:58 CET 2019 Sat Mar 2 21:33:03 CET 2019 % killThread clock
printProc uses externalPrint from haskeline to print the output of a shell command to the console without disturbing the prompt. The OS module in this example simply exports all executables as haskell functions, thanks to the TH magic from Shh.
I am relying on a pretty crude hack to make this work:
(rFd, wFd) <- liftIO createPipe eprint <- getExternalPrint -- from haskeline liftIO . forkIO . forever $ do (s, bc) <- fdRead rFd 1024 eprint s -- ... -- define a function in the interpreter using hint runStmt $ "let externalPrint s = fdWrite (read " <> show (show wFd) <> ") s >> pure ()"
This hack is basically the whole magic of my own hand-rolled uGHCi.
I'd love to not reinvent the wheel there, and just be able to use standard GHCi to make use of externalPrint.
Question is, would a similar thing be possible to implement in GHCi directly, and if so, what would be required to make this work? I am likely far too much a rooky to get this working on my own, so I am asking for help. What steps should I follow to eventually achieve my goal? I guess submitting a feature request would be a start. However, I want progress, so I am wondering:
* The pipe trick is likely too hacky for GHCi. Are there any other portable alternatives for getting data from within the interpreter to the haskell process running it? * Or is there a way to serialize an IO action into the interpreter that I've missed?
The problem here is the boundary between the process that runs the interpreter, and the interpreter itself. I am a bit whacky on terminology here, but as I see it, getExternalPrint returns a function that has internal state. So it isn't really possible to make such a function available from within the interpreter. Hence, the pipe hack above, which just sends the *argument* to externalPrint from the interpreter to the process running it.
Any insights that might help me make that available in standard GHCi? I really think this is a pretty unique feature that would enable all sorts of interesting interactive code.
[1] Haskeline < 0.8.0 doesn't allow to combine IntterpreterT from hint with InputT because of the way exceptions are done. The master branch of haskeline fixes that, so finally you can have a transformer stack that combines both, allowing for pretty simple interactive haskell interpreters with readline functionality. Thanks for that!
-- CYa, ⡍⠁⠗⠊⠕ _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
-- brandon s allbery kf8nh allbery.b@gmail.com