#lang racket/base (require (for-syntax racket/base) racket/runtime-path racket/promise) (provide lazy-require (for-syntax #%datum)) (define-syntax (lazy-require stx) (syntax-case stx () [(lazy-require [modpath (thing ...)] ...) #`(begin (define-namespace-anchor anchor) (lazy-require1 modpath (thing ...) anchor #,stx) ...)])) (define-syntax (lazy-require1 stx) (syntax-case stx () [(lazy-require1 modpath (name ...) anchor orig-stx) (with-syntax ([(defn ...) (for/list ([name (in-list (syntax->list #'(name ...)))]) (unless (identifier? name) (raise-syntax-error #f "expected identifier" #'orig-stx name)) #`(define #,name (make-lazy-function '#,name get-sym)))]) #'(begin (define-runtime-module-path-index mpi-var modpath) (define (get-sym sym) (parameterize ((current-namespace (namespace-anchor->namespace anchor))) (dynamic-require mpi-var sym))) defn ...))])) (define (make-lazy-function name get-sym) ;; Use 'delay/sync' because 'delay' promise is not reentrant. ;; FIXME: OTOH, 'delay/sync' promise is not kill-safe. (let ([fun-p (delay/sync (get-sym name))]) (procedure-rename (make-keyword-procedure (lambda (kws kwargs . args) (keyword-apply (force fun-p) kws kwargs args))) name)))