So I got a bug report that Typed Racket type info is appearing twice,
in the REPL for my emacs racket-mode:
https://github.com/greghendershott/racket-mode/issues/21

All the relevant code is there, but I'll do a quick recap in this post.

Given this sample program:

#lang racket/base
(require racket/sandbox)

;; 1. Run this.
;; 2. In the resulting REPL, enter "1".
;; Result: The type information is printed twice:
;;     - : Integer [more precisely: One]
;;     - : Integer [more precisely: One]
;;     1

(call-with-trusted-sandbox-configuration
 (lambda ()
   (parameterize ([current-namespace (make-empty-namespace)]
                  [sandbox-input (current-input-port)]
                  [sandbox-output (current-output-port)]
                  [sandbox-error-output (current-error-port)])
     (define e (make-evaluator 'typed/racket)) ;; [^note]
     (parameterize ([current-eval e])
       (read-eval-print-loop)))))

;; [note]: Same behavior instead using make-module-evaluator:
;;   (define e (make-module-evaluator '(module m typed/racket 1)))

You get this kind of interaction.

> 1
- : Integer [generalized from One]
- : Integer [generalized from One]
1

Commenting out the sandbox-output param, so that output goes nowhere,
would give you this instead:

> 1
1

IOW the REPL prints the result, but Typed Racket's #%top-interaction
prints the type through a `display` side-effect. (Which is reasonable,
and what any similar #%top-interaction would do, IIUC.)

After lots of code spelunking and experimentation, I discovered that
using an alternative to read-eval-print-loop gets the desired
behavior:

(define (alternative-read-eval-print-loop)
  (let repl-loop ()
    ;; This prompt catches all error escapes, including from read and print.
    (call-with-continuation-prompt
     ;; 1. proc
     (lambda ()
       (let ([v ((current-prompt-read))])
         (unless (eof-object? v)
           (call-with-values
               (lambda ()
                 ;; This prompt catches escapes during evaluation.
                 ;; Unlike the outer prompt, the handler prints
                 ;; the results.
                 (call-with-continuation-prompt
                  (lambda ()
                    ((current-eval) v) ;; <== JUST DO THIS, NOT THE FOLLOWING...
                    ;; (let ([w (cons '#%top-interaction v)])
                    ;;   ((current-eval) (if (syntax? v)
                    ;;                       (namespace-syntax-introduce
                    ;;                        (datum->syntax #f w v))
                    ;;                       w)))
                    )))
             (lambda results (for-each (current-print) results)))
           ;; Abort to loop. (Calling `repl-loop' directory would not
be a tail call.)
           (abort-current-continuation (default-continuation-prompt-tag)))))
     ;; 2. prompt-tag
     (default-continuation-prompt-tag)
     ;; 3. handler
     (lambda args (repl-loop)))))

After puzzling why, finally I noticed in sandbox.rkt a reference to
#%top-interaction that I'd overlooked previously:

https://github.com/plt/racket/blob/533a2f21f864a25e27e744ef2fef04343e1d02e4/pkgs/sandbox-lib/racket/sandbox.rkt#L858

~~~~~~~~~~~~~~~~~~~

So: I _think_ I've found a reasonable work-around -- use my
alternative-read-eval-print-loop with racket/sandbox.

However I wanted to share this in case any Racket core devs might want
to advise, "Yeah, no, you really don't want to do that, and here's
why...".  Anyone?
____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to