racket / rackunit

Other
18 stars 34 forks source link

`require/expose` does not work on macros #152

Open lihebi opened 3 years ago

lihebi commented 3 years ago

require/expose can require an unprovided binding from a module. But it only works on functions, not macros. E.g.

(module aaa racket
  (define (foo a b) (+ a b))
  (define-syntax-rule (myadd x y)
    (+ x y)))
(require rackunit)
;; OK
(require/expose 'aaa (foo))
;; ERROR: myadd: use does not match pattern: (myadd x y)
(require/expose 'aaa (myadd))

The relative code blocks implementing require/expose:

https://github.com/racket/rackunit/blob/6c04ee267d258a13f9c5cbc437dac60234e54ef4/rackunit-lib/rackunit/private/util.rkt#L72-L77

The reason is quite simple: transformer bindings are not available at runtime thus (apply eval names) thows error. Is it possible to make require/expose work on macros?

jackfirth commented 3 years ago

I don't think it's possible, nor do I think it's something we want to encourage in the first place. What were you planning to use this functionality for?

lihebi commented 3 years ago

Thanks for your reply.

I'm developing a module-aware IDE for racket. In particular, a user develops code in one module (A) and requires it in another (B), and keeps doing this without restarting the racket runtime. Without require/expose, one has to re-declare module A every time a new function/macro is added to A, which is not very efficient when module A becomes large.

jackfirth commented 3 years ago

I don't think you need require/expose for that. You can use dynamic-rerequire instead, possibly combined with all-defined-out. You might also be interested in racket-reloadable, which uses dynamic-rerequire to implement hot code reloading for webserver-like programs.

lihebi commented 3 years ago

Thanks for the pointers! If I understand correctly, you mean:

;; define module A with all-defined-out
(module A racket
  (provide (all-defined-out))
  (define a 1))
;; dynamically add a new binding b
(enter! 'A)
(define b 2)
(enter! #f)

;; try to reload
(dynamic-rerequire ''A)

;; OK
(dynamic-require ''A 'a)

;; ERROR: dynamic-require: name is not provided, name: 'b
(dynamic-require ''A 'b)

;; OK
(require/expose 'A (b))

Looks like this is not working, all-defined-out cannot pick up a new dynamically added binding. b.