Skip to content

Commit

Permalink
add binding-space support to provide and require
Browse files Browse the repository at this point in the history
The intent of this change is to better support languages besides plain
Racket where multiple binding spaces (within a phase) makes sense.

The existing `make-interned-syntax-introducer` function supports a
binding-space convention where a scope (keyed on a symbol name) can be
used consistently across modules to create bindings with that scope.
This commit adds more direct support for binding spaces to `require`
and `provide`. A `for-space` form allows a module to provide a binding
in a space, and then `require` will (by default) create a binding in
an importing context using the space's scope.

The changes to `require` and `provide` are mostly internal, but the
result of a function like `identfier-binding` might need to include
require/provide space information as well as require/provide phase
information. To minimize changes to existing code, space information
is combined with phase information in a single value, and the default
space (i.e., no extra space scope) is represented by just using the
phase value. In other words, the representation of import and export
information is unchanged unless binding spaces are specifically used.
Require and provide transformers similarly need to change to work with
binding spaces, but existing transformers continue to work as long as
they are not combined with binding-space operations.

The representation for combined phase and space information is
supported by a new `racket/phase+space` library, which provides
functions like `phase+space?`. Using "phase+space" seems a bit on the
nose, but after trying alternatives and considering that further
extensions seem unlikely, I ended up preferring "phase+space".
  • Loading branch information
mflatt committed Jul 18, 2021
1 parent 2e1756a commit 0d526f5
Show file tree
Hide file tree
Showing 34 changed files with 7,654 additions and 5,336 deletions.
2 changes: 1 addition & 1 deletion pkgs/base/info.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

;; In the Racket source repo, this version should change only when
;; "racket_version.h" changes:
(define version "8.2.0.2")
(define version "8.2.0.3")

(define deps `("racket-lib"
["racket" #:version ,version]))
Expand Down
1 change: 1 addition & 0 deletions pkgs/racket-doc/scribblings/reference/macros.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ called.
@include-section["stx-serialize.scrbl"]
@include-section["include.scrbl"]
@include-section["syntax-util.scrbl"]
@include-section["phase+space.scrbl"]
42 changes: 24 additions & 18 deletions pkgs/racket-doc/scribblings/reference/module-reflect.scrbl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#lang scribble/doc
@(require "mz.rkt"
(for-label compiler/embed
syntax/modresolve))
syntax/modresolve
racket/phase+space))

@title{Module Names and Loading}

Expand Down Expand Up @@ -405,15 +406,16 @@ the module's explicit imports.}

@defproc[(module-compiled-exports [compiled-module-code compiled-module-expression?]
[verbosity (or/c #f 'defined-names) #f])
(values (listof (cons/c (or/c exact-integer? #f) list?))
(listof (cons/c (or/c exact-integer? #f) list?)))]{
(values (listof (cons/c phase+space? list?))
(listof (cons/c phase+space? list?)))]{

Returns two association lists mapping @tech{phase level} values (where
@racket[#f] corresponds to the @tech{label phase level}) to exports at
the corresponding phase. The first association list is for exported
Returns two association lists mapping from a combination of @tech{phase level}
and @tech{binding space} to exports at
the corresponding phase and space. The first association list is for exported
variables, and the second is for exported syntax. Beware however, that
value bindings re-exported though a @tech{rename transformer} are in
the syntax list instead of the value list.
the syntax list instead of the value list. See @racket[phase+space?]
for information on the phase-and-space representation.

Each associated list, which is represented by @racket[list?] in the
result contracts above, more precisely matches the contract
Expand All @@ -423,9 +425,9 @@ result contracts above, more precisely matches the contract
(listof
(or/c module-path-index?
(list/c module-path-index?
(or/c exact-integer? #f)
phase+space?
symbol?
(or/c exact-integer? #f))))
phase+space?)))
(code:comment @#,elem{only if @racket[verbosity] is @racket['defined-names]:})
symbol?))
]
Expand All @@ -449,12 +451,14 @@ than the name that is exported).
For each origin, a @tech{module path index} by itself means that the
binding was imported with a @tech{phase level} shift of @racket[0]
(i.e., a plain @racket[require] without @racket[for-meta],
@racket[for-syntax], etc.), and imported identifier has the same name
@racket[for-syntax], etc.) into the default @tech{binding space} (i.e.,
without @racket[for-space]), and the imported identifier has the same name
as the re-exported name. An origin represented with a list indicates
explicitly the import, the import @tech{phase level} shift (where
@racket[#f] corresponds to a @racket[for-label] import), the import
name of the re-exported binding, and the @tech{phase level} of the
import.
explicitly the import, the @tech{phase level} plus @tech{binding space}
where the imported identifier is bound (see @racket[phase+space?] for more
information on the representation), the symbolic name of the import
as bound in the importing module, and the @tech{phase level} plus
@tech{binding space} of the identifier from the exporting module.

@examples[#:eval mod-eval
(module-compiled-exports
Expand All @@ -473,7 +477,8 @@ import.
(define compile-time (current-seconds)))))
'defined-names)]

@history[#:changed "7.5.0.6" @elem{Added the @racket[verbosity] argument.}]}
@history[#:changed "7.5.0.6" @elem{Added the @racket[verbosity] argument.}
#:changed "8.2.0.3" @elem{Generalized results to phase--space combinations.}]}



Expand Down Expand Up @@ -707,8 +712,8 @@ A module can be @tech{declare}d by using @racket[dynamic-require].
[mod (or/c module-path? module-path-index?
resolved-module-path?)]
[verbosity (or/c #f 'defined-names) #f])
(values (listof (cons/c (or/c exact-integer? #f) list?))
(listof (cons/c (or/c exact-integer? #f) list?)))]{
(values (listof (cons/c phase+space? list?))
(listof (cons/c phase+space? list?)))]{

Like @racket[module-compiled-exports], but produces the
exports of @racket[mod], which must be @tech{declare}d (but
Expand All @@ -724,7 +729,8 @@ A module can be @tech{declare}d by using @racket[dynamic-require].
(define bush (* 2 pi)))
(module->exports ''banana)]

@history[#:changed "7.5.0.6" @elem{Added the @racket[verbosity] argument.}]}
@history[#:changed "7.5.0.6" @elem{Added the @racket[verbosity] argument.}
#:changed "8.2.0.3" @elem{Generalized results to phase--space combinations.}]}


@defproc[(module->indirect-exports
Expand Down
97 changes: 97 additions & 0 deletions pkgs/racket-doc/scribblings/reference/phase+space.scrbl
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#lang scribble/doc
@(require "mz.rkt"
(for-label racket/phase+space))

@title[#:tag "phase+space"]{Phase and Space Utilities}

This comment has been minimized.

Copy link
@jackfirth

jackfirth Aug 6, 2021

Contributor

What do you think of calling the pair of a phase and a binding space a "binding location" instead of a phase+space?

This comment has been minimized.

Copy link
@mflatt

mflatt Aug 6, 2021

Author Member

Although “location” seems too close to “source location”, I think there are words that could work, or we could make up a word. (My second-favorite approach when working on this was “import–export coordinate”, abbreviated as iecoord.) But whatever the word, then you have to learn that word, in addition to learning “phase” and “space”. I still lean toward phase+space, because then there's nothing new to learn for this relatively unimportant corner, but I'm not strongly attached to that position.


@note-lib-only[racket/phase+space]

The @racketmodname[racket/phase+space] library provides functions for
manipulating combined representations of @tech{phase levels} and
@tech{binding spaces}, particularly as used for @tech{require
transformers} and @tech{provide transformers}.

When @racket[identifier-binding] (and related functions, like
@racket[identifier-transformer-binding]),
@racket[syntax-local-module-exports],
@racket[syntax-local-module-required-identifiers],
@racket[module-compiled-exports], or @racket[module->exports] produces
a phase--space combination (or phase--space shift combination), then
two such values that are @racket[equal?] will be @racket[eqv?].

@history[#:added "8.2.0.3"]

@defproc[(phase? [v any/c]) boolean?]{

Returns @racket[#f] if @racket[v] is a valid representation of a

This comment has been minimized.

Copy link
@greghendershott

greghendershott Jul 20, 2021

Contributor

Should this be "Returns #t if v is a valid representation..." -- returns true not false?

Likewise for the next few predicates.

@tech{phase level}: either an exact integer representing a numbered
phase level or @racket[#f] representing the @tech{label phase level}.}

@defproc[(space? [v any/c]) boolean?]{

Returns @racket[#f] if @racket[v] is a valid representation of a
@tech{binding space}: either a symbol representing the space whose
scope is accessed via @racket[make-interned-syntax-introducer], or
@racket[#f] representing the default binding space.}

@defproc[(phase+space? [v any/c]) boolean?]{

Returns @racket[#f] if @racket[v] is a valid representation of a
@tech{phase level} and @tech{binding space} combination. The possible
representations are as follows:

@itemlist[

@item{a phase (in the sense of @racket[phase?]) by itself, which
represents that phase plus the default binding space}

@item{a pair whose @racket[car] is a phase and whose @racket[cdr] is
a non-@racket[#f] space (in the sense of @racket[space?])}

]}

@deftogether[(
@defproc[(phase+space-phase [p+s phase+space?]) phase?]
@defproc[(phase+space-space [p+s phase+space?]) phase?]
)]{

Extracts the @tech{phase level} or @tech{binding space} component from
a combination.}


@defproc[(phase+space-shift? [v any/c]) boolean?]{

Returns @racket[#f] if @racket[v] is a valid representation of a
@tech{phase level} shift and @tech{binding space} shift combination. A
shift can be applied to a combination of a phase level and binding
space using @racket[phase+shift+]. The possible representations of a
shift are as follows:

@itemlist[

@item{exact integer --- represents an amount to shift a phase level
and no change to the binding space}

@item{@racket[#f] --- represents a shift to the @tech{label phase level}
and no change to the binding space}

@item{a pair whose @racket[car] is an exact integer or @racket[#f],
and whose @racket[cdr] is a space (in the sense of
@racket[space?]) --- represents a phase level shift in the
@racket[car] and a change to the binding space that is in the
@racket[cdr]}

]}

@defproc[(phase+space+ [p+s phase+space?] [shift phase+space-shift?])
phase+space?]{

Applies @racket[shift] to @racket[p+s] to produce a new combination of
@tech{phase level} and @tech{binding space}.}

@defproc[(phase+space-shift+ [shift phase+space?] [additional-shift phase+space-shift?])
phase+space-shift?]{

Composes @racket[shift] and @racket[additional-shift] to produce a new
shift that behaves the same as applying @racket[shift] followed by
@racket[additional-shift].}
45 changes: 27 additions & 18 deletions pkgs/racket-doc/scribblings/reference/stx-comp.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ is @racket[#f].}
module-path-index?
symbol?
exact-nonnegative-integer?
(or/c exact-integer? #f)
(or/c exact-integer? #f))
phase+space-shift?
phase+space?)
(list/c symbol?))]{

Returns one of three (if @racket[top-level-symbol?] is @racket[#f])
Expand All @@ -121,7 +121,7 @@ or four (if @racket[top-level-symbol?] is true) kinds of values, depending on th

@item{The result is a list of seven items when @racket[id-stx]
has a @tech{module binding}: @racket[(list _from-mod _from-sym
_nominal-from-mod _nominal-from-sym _from-phase _import-phase
_nominal-from-mod _nominal-from-sym _from-phase _import-phase+space-shift
_nominal-export-phase)].

@itemize[
Expand Down Expand Up @@ -168,14 +168,16 @@ or four (if @racket[top-level-symbol?] is true) kinds of values, depending on th
representing the originating phase. For example, it is
@racket[1] if the definition is for-syntax.}

@item{@racket[_import-phase] is @racket[0] if the binding
@item{@racket[_import-phase+space-shift] is @racket[0] if the binding
import of @racket[_nominal-from-mode] is from a definition
or a plain @racket[require], @racket[1] if it is from a
@racket[for-syntax] import, etc.}
@racket[for-syntax] import, a phase combined with a space name if it
is from a @racket[for-space] import, etc.}

@item{@racket[_nominal-export-phase] is the @tech{phase level}
@item{@racket[_nominal-export-phase+space] is the @tech{phase level}
and @tech{binding space}
of the export from @racket[_nominal-from-mod] for an
imported binding or the phase level of the definition for a
imported binding, or it is the phase level of the definition for a
binding from the enclosing module of @racket[id-stx].}

]}
Expand All @@ -201,7 +203,8 @@ transformer, so that @racket[identifier-binding] is consistent with
@racket[free-identifier=?].

@history[#:changed "6.6.0.4" @elem{Added the @racket[top-level-symbol?] argument to report
information on top-level bindings.}]}
information on top-level bindings.}
#:changed "8.2.0.3" @elem{Generalized phase results to phase--space combinations.}]}


@defproc[(identifier-transformer-binding [id-stx identifier?]
Expand All @@ -214,10 +217,12 @@ transformer, so that @racket[identifier-binding] is consistent with
module-path-index?
symbol?
exact-nonnegative-integer?
(or/c exact-integer? #f)
(or/c exact-integer? #f)))]{
phase+space-shift?
phase+space?))]{

Same as @racket[(identifier-binding id-stx (and rt-phase-level (add1 rt-phase-level)))].}
Same as @racket[(identifier-binding id-stx (and rt-phase-level (add1 rt-phase-level)))].

@history[#:changed "8.2.0.3" @elem{Generalized phase results to phase--space combinations.}]}


@defproc[(identifier-template-binding [id-stx identifier?])
Expand All @@ -227,11 +232,13 @@ Same as @racket[(identifier-binding id-stx (and rt-phase-level (add1 rt-phase-le
symbol?
module-path-index?
symbol?
exact-nonnegative-integer?
(or/c exact-integer? #f)
(or/c exact-integer? #f)))]{
phase+space?
phase+space-shift?
phase+space?))]{

Same as @racket[(identifier-binding id-stx (sub1 (syntax-local-phase-level)))].}
Same as @racket[(identifier-binding id-stx (sub1 (syntax-local-phase-level)))].

@history[#:changed "8.2.0.3" @elem{Generalized phase results to phase--space combinations.}]}


@defproc[(identifier-label-binding [id-stx identifier?])
Expand All @@ -242,10 +249,12 @@ Same as @racket[(identifier-binding id-stx (sub1 (syntax-local-phase-level)))].}
module-path-index?
symbol?
exact-nonnegative-integer?
(or/c exact-integer? #f)
(or/c exact-integer? #f)))]{
phase+space-shift?
phase+space?))]{

Same as @racket[(identifier-binding id-stx #f)].

Same as @racket[(identifier-binding id-stx #f)].}
@history[#:changed "8.2.0.3" @elem{Generalized phase results to phase--space combinations.}]}


@defproc[(identifier-binding-symbol [id-stx identifier?]
Expand Down

0 comments on commit 0d526f5

Please sign in to comment.