Chaining module languages

Chaining module languages

Georges Dupéron <[email protected]>

 (require chain-module-begin)
  package: chain-module-begin

This package is experimental. Later versions may break backward-compatibility.


(chain-module-begin lang . body)

This macro is intended to be used as the result of a #%module-begin macro. It chain-calls the #%module-begin of lang. This makes it possible for a #%module-begin to perform some changes on its body, and then chain-call the #%module-begin of a user-specified language.

As an example here is the definition for a no-op language, which simply takes a (possibly improper) list of languages to chain, and calls the next one:

(module the-meta-lang racket/base
  (provide (rename-out [new-#%module-begin #%module-begin]))
  (require chain-module-begin
           (for-syntax racket/base
  (define-syntax (new-#%module-begin stx)
    (syntax-parse stx
      [(_ {~or next-lang:id (next-lang:id . chain₊)} . body)
       (define maybe-chain₊ (if (attribute chain₊)
       (define new-form `(,#'chain-module-begin ,#'next-lang ,@maybe-chain₊
                                                . ,(transform-body #'body)))
       (datum->syntax stx new-form stx stx)]))
  (define-for-syntax (transform-body body)
    ; identity transformation:

This language could then be used as follows:

(module b the-meta-lang typed/racket
  (define x : Number 123))

Given two other meta-language built in the same way and provided by meta-two and meta-three, it would be possible to chain the three languages as follows:

(module b the-lang (meta-two meta-three . typed/racket)
  (define x : Number 123))

The chain-module-begin macro produces the following syntax:

 (require lang)
 (continue . internal-args))

where (continue . internal-args) fully expands (#%module-begin . body), where #%module-begin is the one provided by lang, and produces the following syntax:

(begin . expanded-body)

An extra scope is added to the whole (begin . expanded-body) form, so that a #%require form within the expanded-body may shadow bindings provided by lang, just as require forms normally have the possibility to shadow bindings provided by the #lang language.