Polyvalent identifiers with multi-id
define-multi-id
7.7

Polyvalent identifiers with multi-id

Georges Dupéron

 (require multi-id) package: multi-id

This library is implemented using literate programming. The implementation details are presented in the Implementation of the multi-id library document.

This package helps defining identifiers with many different meanings in different contexts. An identifier can be given a meaning:

syntax

(define-multi-id name
  maybe-type-expander
  maybe-match-expander
  maybe-maybe-set!+call+id
  fallback-clause ...)
 
maybe-type-expander = 
  | #:type-expander proc
     
maybe-match-expander = 
  | #:match-expander proc
     
maybe-set!+call+id = 
  | #:set!-transformer proc
  | else
  | maybe-set! maybe-call maybe-id
     
maybe-set! = #:set! proc
     
maybe-call = #:call proc
  | #:call-id identifier
     
maybe-id = #:id proc
  | #:id-id identifier
     
else = #:else-id identifier
  | #:mutable-else-id identifier
  | #:else identifier-expression
  | #:mutable-else identifier-expression
     
fallback-clause = #:??? expression
     
??? = "any struct-type-property?, without the prop:"
Defines name as a type expander, match expander, set! transformer, identifier macro, a regular macro, some other concepts, each implemented with an arbitrary struct-type-property?, or combinations of those.

In the syntax described above, each proc should be a transformer procedure accepting a single syntax? argument and returning a syntax? value, i.e. the signature of each proc should be (-> syntax? syntax?). Each identifier should be an identifier, and each identifier-expression should be a compile-time expression producing an identifier.

The following options are currently supported:
#:??? expression
 
??? = "any struct-type-property?, without the prop:"

The identifier name is a struct with the prop:??? struct type property, using the given expression

In the syntax above, #:??? is only a placeholder; any keyword can be used, so long as prefixing the keyword’s name with prop: gives an identifier which is a struct-type-property?.

#:type-expander proc

The identifier name acts as a type expander, using the given proc which should return the syntax for a type.

#:match-expander proc

The identifier name acts as a match expander, using the given proc which should return the syntax for a match pattern.

#:set!-transformer proc

The identifier name acts as a set! transformer, using the given proc which should return a syntax? value. The proc is used both when name is used in a set! form, and when it is used as a macro or identifier macro.

#:set! proc

The identifier name acts as a set! transformer when it is used in a set! form, using the given proc which should return a syntax? value.

The proc is used only when name is used in a set! form, but not when it is used as a macro or identifier macro. In these cases, #:call and #:id, respectively, are used instead.

If #:id is not specified, but name is used as an identifier macro, the exn:fail:syntax exception is raised. If #:call is not specified, but name is used as a regular macro, the exn:fail:syntax exception is raised.

#:call proc

The identifier name acts as a macro when it appears in the first position of a form, using the given proc which should return a syntax? value.

The proc is used only when name is used as a regular macro, but not when it is used as an identifier macro or when it is used in a set! form. In these cases, #:id and #:set!, respectively, are used instead.

If #:set! is not specified, but name is used in a set! form, the exn:fail:syntax exception is raised. If #:id is not specified, but name is used as an identifier macro, the exn:fail:syntax exception is raised.

#:call-id identifier

The identifier name acts as a macro when it appears in the first position of a form. The occurrence of name is replaced by the given identifier, which should either be a function or a macro.

When name is used as a macro, i.e. in a form like (name . args), the whole form is replaced by (identifier . args). If the identifier is itself a regular macro, then the whole (identifier . args) form is expanded.

The identifier is used only when name is used as a regular macro, not when it is used as an identifier macro or as a set! transformer. In these cases, #:id and #:set!, respectively, are used instead.

If #:set! is not specified, but name is used in a set! form, the exn:fail:syntax exception is raised. If #:id is not specified, but name is used as an identifier macro, the exn:fail:syntax exception is raised.

#:id proc

The identifier name acts as an identifier macro, using the given proc which should return a syntax? value.

The proc is used only when name is used as an identifier macro, but not when it appears in the first position of a form, nor when it is used in a set! form. In these cases, #:call and #:set!, respectively, are used instead.

If #:set! is not specified, but name is used in a set! form, the exn:fail:syntax exception is raised. If #:call is not specified, but name is used as a regular macro, the exn:fail:syntax exception is raised.

#:id-id proc

The identifier name acts as an identifier macro. The occurrence of name is replaced by the given identifier. If the identifier is itself an identifier macro, it is expanded again.

The identifier is used only when name is used as an identifier macro, but not when it appears in the first position of a form, nor when it is used in a set! form. In these cases, #:call and #:set!, respectively, are used instead.

If #:set! is not specified, but name is used in a set! form, the exn:fail:syntax exception is raised. If #:call is not specified, but name is used as a regular macro, the exn:fail:syntax exception is raised.

#:else-id identifier

The identifier name acts as a regular macro and as an identifier macro. The occurrence of name is replaced by the given identifier, which should either be a function, or be both a macro and an identifier macro at the same time.

When name is used as an identifier macro, it is replaced by identifier. If the identifier is itself an identifier macro, then it is expanded.

When name is used as a macro, i.e. in a form like (name . args), the whole form is replaced by (identifier . args). If the identifier is itself a regular macro, then the whole (identifier . args) form is expanded.

The identifier is not used when name is used in a set! form, instead the exn:fail:syntax exception is raised.

#:mutable-else-id identifier

The identifier name acts as a regular macro, as an identifier macro and as a set! transformer. In all three cases, the occurrence of name is replaced by the given identifier, which should either be a function, or be a macro and an identifier macro and a set! transformer at the same time.

This option works like #:else-id, except that name can also be used in a set! form.

When name is used in a set! form like (set!. (name . vals)), the whole form is replaced by (set!. (identifier . vals)). If the identifier is itself a set! transformer, then the whole (set!. (identifier . vals)) form is expanded.

#:else identifier-expression

The identifier name acts as a regular macro and as an identifier macro. The occurrence of name is replaced by the result of the compile-time expression identifier-expression, which should either produce the syntax for a function, or the syntax for an identifier which is both a macro and an identifier macro at the same time.

It is equivalent to #:else-id, except that the identifier is not constant, but is instead produced by identifier-expression. Note that identifier-expression is not a transformer function (it will not be able to access the original syntax). In other words, the compile-time contract for identifier-expression is syntax?, not (-> syntax? syntax?).

The identifier-expression is not used when name is used in a set! form, instead the exn:fail:syntax exception is raised.

#:mutable-else identifier-expression

The identifier name acts as a regular macro, as an identifier macro and as a set! transformer. In all three cases, the occurrence of name is replaced by the result of the compile-time expression identifier-expression, which should either produce the syntax for a mutable identifier containing a function, or the syntax for an identifier which is a macro and an identifier macro and a set! transformer at the same time.

It is equivalent to #:mutable-else-id, except that the identifier is not constant, but is instead produced by identifier-expression. Note that identifier-expression is not a transformer function (it will not be able to access the original syntax). In other words, the compile-time contract for identifier-expression is syntax?, not (-> syntax? syntax?).