How to make top-level depend on module-begin's success?

Does anyone understand the interaction between #%module-begin and #%top-interaction in Racket? E.g. if I do:

#lang racket
(if x y) ;; syntax error, missing else case

And hit "Run" in Dr. Racket, it gives a syntax error about the if, but still gives you a REPL that you can type in.

Is there a way to make it so that you can't get a REPL unless the current file successfully loads?

1 Like

My guess is that this behavior is hard-coded in DrRacket, so there's no way to work around it.

Based on @shhyou's digging, here's a way to do it by arranging for the #%top-interaction to be different if the module is successfully expanded. A more robust implementation could still allow modules written in the language to define #%top-interaction. You can try this with #lang s-exp.

#lang racket
(provide (except-out (all-from-out racket)
                     #%top-interaction
                     #%module-begin)
         (rename-out [no-top-interaction #%top-interaction]
                     [modbegin #%module-begin]))
(define-syntax (no-top-interaction stx)
  (syntax-case stx ()
    [(_ . form)
     (raise-syntax-error #f "interactions disabled for invalid module" stx #'form)]))
(define-for-syntax (yes-top-interaction stx)
  (syntax-case stx ()
    [(_ . form)
     #`(#%top-interaction . form)]))
(define-syntax (modbegin stx)
  (syntax-case stx ()
    [(_ body ...)
     #`(#%module-begin
        (define-syntax #,(datum->syntax stx '#%top-interaction)
          yes-top-interaction)
        body ...)]))
1 Like

FWIW, Captain Obvious, maybe not important:

IIRC DrRacket and Racket Mode already show a message and disable the REPL when #%top-interaction isn't defined at all -- when it's not in namespace-mapped-symbols for a namespace "inside" the module (from module->namespace). Some langs like #lang info (IIRC) do this, always.

So I think it would also work for an expansion conditionally not to define #%top-interaction at all.

Having said that, I think an advantage of your approach is your raise-syntax-error can supply a more precise/informative error message ("no REPL available because your program failed to type check" or whatever). This makes it clearer it's related to the particular user program, not a feature of the module for all user programs.

1 Like

I've wished #lang info would define #%top-interaction!

2 Likes

It has never bothered me, because I misbelieved #lang info was supposed to the moral equivalent of a dictionary or hash-table, where the values are all simple string, number, and list values. At least, that's all that I've done myself, or noticed in examples. Given that, what could the REPL show, that isn't self-evident in the source?

But just now I re-checked the docs and noticed the grammar has a whole section of info-primitive items. So it could be handy to view the resulting values in a REPL, without needing to fire up get-info.

p.s. I suppose the Racket Mode REPL could inject, when missing, a #%top-interaction like the one for racket. But I suspect that would be one of those things where it's 2 minutes to implement the feature itself... and hours to work out how users configure/control/understand when that magic happens, or not. :slight_smile: