[Haskell-cafe] I fell in love with System.Console.Haskeline.getExternalPrint
Mario Lang
mlang at delysid.org
Sun Mar 31 22:50:49 UTC 2019
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,
⡍⠁⠗⠊⠕
More information about the Haskell-Cafe
mailing list