[Haskell-cafe] I fell in love with System.Console.Haskeline.getExternalPrint
mlang at delysid.org
Sun Mar 31 22:50:49 UTC 2019
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 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
-- 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.
 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!
More information about the Haskell-Cafe