Repl delays, background CPU

I've noticed some REPL issues. User perceptible delay and 100%CPU usage (two different issues). See below:

In emacs / racket-mode:

racket-mode> (let loop () (define form (read)) (unless (eq? form 'q) (println form) (loop)))

hello
*****delay*****
'hello
q

In DrRacket:

drracket>  (let loop () (define form (read)) (unless (eq? form 'q) (println form) (loop)))
hello
*****delay*****
'hello
q

On the command line:

[acarrico@russet terms]$ racket 
Welcome to Racket v8.13 [cs].
> (let loop () (define form (read)) (unless (eq? form 'q) (println form) (loop))
)
hello
*****FAST, no delay*****
'hello
hello
'hello
hello
'hello
hello
'hello
q
> 
***laptop fan goes on, .racket-wrapped uses 100% CPU in top****

Anyone else have similar issues? I'm on NixOS.

I got a chance to run my test on a Debian machine, also with Racket v8.13:

racket-mode: racket-mode repl under Debian seems responsive.

DrRacket: I replicate the delay issue under Debian

command line: I replicate the command line CPU 100% issue under Debian.

Maybe I'll open an issue since I've seen it on two machines.

This may be related (but I have no idea why DrRacket would be affected)

Yes. That is one of the issues, also this report:

Concurrent infinite loops and odd behavior from `read` in REPLs · Issue #4612 · racket/racket · GitHub

This seems to be a real thing, so discussion has moved to issue 4612. Greg Hendershott has addressed the delay issue in racket-mode issue 749.

As I mentioned in the racket-mode thread, but should probably mention here for the community. I figured out a great way to run a command loop in emacs. Instead of hitting F5 to enter the racket-mode repl, run C-u M-x compile. This will run your command loop in comint mode, so you get a lot of features for free. Adding the C-u prefix gives two-way prompt style communication with your program. In this mode, the history is your command loop's own history (rather than the Racket repl history), which is a very nice feature to get for free.

I suppose another possible design would be for the user program's input/output to get its own dedicated UI -- such as a completely different buffer in Emacs.

IIRC DrRacket sort of does this for user program input -- a temporary input box appears -- but the resulting input and all user program output still goes in the interactions pane.

(I actually considered that kind of UX but it seemed impossible to achieve well with a separate front end process in Emacs. For Emacs, I think it would be cleaner to use a dedicated I/O buffer, which could e.g. be put in comint-mode as @acarrico said or otherwise customized differently.)


Some existing precedent for this split is logger output: In command-line Racket, this gets written to stdout -- but in both DrR and Racket Mode, this gets its own UI. Since logger output is async, this is particularly helpful.

Having said that, I've heard from one user who doesn't love the separated logger output. There are tradeoffs. The command line approach, with everything dumped together, can feel more immediate and complete, and regardless of async there is some useful sense of interleaved time-ordering. (And some people may not care that the resulting mix is "hopeless", and the original structure can only be recovered through unreliable regexps).

Maybe the "most correct" UI would use one window/pane, time flowing down, and separate columns for REPL per se, user I/O, logger, etc. That would avoid "hiding" anything, preserve time order, and preserve structure. How usable it would be... idk.

Ah, this is a nice idea! We didn't consider segregating the user program output from the REPL output! That would have made the implementation simpler too, I believe.

You do give them different colors!

Welcome to Racket v8.13 [cs].
————— run main.rkt —————
main.rkt> (display "hello> ")
main.rkt> (read)
'#(1 2 3)
main.rkt> 
————— stdio ————————
hello> #(1 2 3)



————— stderr ————————
—————————————————————

Bonus points for raw mode. But actually just choosing among racket-repl or comint is working pretty well for me. Life would compete if I figured out how to launch into term-mode if I needed raw i/o.