Macros: Useful macros and macro-writing utilities

_Macros_: Useful macros and macro-writing utilities

By Ryan Culpepper
(ryanc at plt-scheme dot org)

This package provides useful syntactic extensions (macros) as well as
procedures helpful for writing macros.

_struct.ss_: Defining struct types

Imported with:
  (require (planet "" ("ryanc" "macros.plt" 1)))

PLT Scheme struct types created with 'make-struct-type' may have
user-defined properties, immutable fields, and automatic field
values. They may also act as procedures. This package defines a macro
that provides access to these features with the convenience of

> define-struct* SYNTAX

  (define-struct* name (field-spec ...) struct-option ...)

  field-spec is one of:
    - field-name
    - (field-name (field-flag ...))
    - (field-name (field-flag ...) accessor-name mutator-name)

  field-flag is one of:
    - #:immutable
    - #:auto

  struct-option is one of:
    - (#:super super-struct-name)
    - (#:super-struct struct-type-descriptor-expr)
    - (#:auto-value expr)
    - (#:property key-expr value-expr)
    - (#:inspector expr)
    - #:transparent
    - (#:procedure procedure-expr)
    - (#:procedure-field field-name)
    - (#:guard guard-expr)

  The 'define-struct*' form defines static struct information, a
  struct type descriptor, a predicate, a constructor, field accessors,
  and field mutators like 'define-struct', with the following
  exceptions and modifications:

  Field accessors and mutators follow the standard naming convention
  '<struct>-<field>' and 'set-<struct>-<field>!' by default, unless
  specific names are given in the field-spec.

  Fields with the '#:immutable' flag are immutable.

  Fields with the '#:auto' flag have automatic values. The constructor
  does not take an argument for an auto field. A non-auto field may
  not follow an auto field.

  'define-struct*' provides two ways of creating a substruct.  You may
  supply the name of the super struct (which must be in scope and
  bound to the static struct info created by 'define-struct' or
  'define-struct*') in a '#:super' declaration, or you may use the
  '#:super-struct' option and provide the super struct type descriptor
  (usually named 'struct:<name>') as a value.

  The '#:auto-value' option determines the value assigned to auto
  fields when an instance is created.

  Each '#:property' declaration adds a property key-value pair to the
  struct type.

  The '#:inspector' declaration determines the inspector that
  controlls access to the struct. The '#:transparent' declaration is
  equivalent to an inspector of #f: the resulting struct is
  transparent to all inspectors.

  Instances of the struct type can be made to act as procedures in one
  of two ways: the '#:procedure' option or the '#:procedure-field'

  The '#:procedure' declaration takes a single procedure to use for
  all instances. The procedure is passed n+1 arguments, where the
  first argument is the instance and the other n arguments are from
  the application.

  The '#:procedure-field' declaration must be followed by a field
  name; applying the struct to n arguments is equivalent to applying
  that field of the struct to those n arguments.

  The '#:guard' declaration controls the creation of substructs.

> define-struct-property SYNTAX

  (define-struct-property name)

  Defines 'name' as a struct property, 'name?' as a predicate for
  struct types and instances, and 'name-value' as an accessor for
  struct types and instances that have that property.

> define-struct-like SYNTAX

  (define-struct-like name (field ...) expr)

  Sometimes it is useful to associate additional attributes with an
  existing datatype rather than create a new struct type that directly
  incorporates those attributes as fields. 'define-struct-like'
  provides a struct-like convenience interface for this pattern by
  creating a weak hashtable mapping struct "representatives" to their
  extended attributes.

  The constructor takes a value for each field and returns the result
  of the representative expression. Each execution of 'expr' should
  result in a newly-allocated value. If 'expr' returns a
  representative value already in the backing hashtable, the
  constructor raises an error.

  The predicate recognizes representatives created through the

  The field accessors and mutators modify the extra attributes. They
  do not affect the representative.

  This form does not define match-compatible static information. It
  may in a later version.

_stx.ss_: Syntax utilities

Imported with: 
  (require (planet "" ("ryanc" "macros.plt" 1)))

but more often with:
  (require-for-syntax (planet "" ("ryanc" "macros.plt" 1)))

The "" module contains procedures and syntactic forms useful for
implementing macros (thus the use of 'require-for-syntax' above).

In addition, "" re-exports everything provided from
(lib "" "syntax"), so you do not need to import both.

> stx-caar : syntax -> syntax
> stx-cadr : syntax -> syntax
> stx-cdar : syntax -> syntax
> stx-cddr : syntax -> syntax

  Accessors for syntax-lists.

> stx-keyword? : any -> boolean
> stx-string? : any -> boolean
> stx-bytes? : any -> boolean
> stx-vector? : any -> boolean
> stx-number? : any -> boolean
> stx-exact? : any -> boolean
> stx-inexact? : any -> boolean
> stx-rational? : any -> boolean
> stx-real? : any -> boolean
> stx-integer? : any -> boolean
> stx-positive-integer? : any -> boolean
> stx-nonnegative-integer? : any -> boolean
> stx-boolean? : any -> boolean
> stx-true? : any -> boolean
> stx-false? : any -> boolean
> stx-nonfalse? : any -> boolean

  Predicates on the contents of syntax objects. For example:

    (stx-keyword? #'#:kw) = #t
    (stx-string? #'"here") = #t
    (stx-number? #'10.3) = #t

  Note: 'stx-integer?', 'stx-positive-integer?', and
  'stx-nonnegative-integer?' only return true on exact integers, in
  contrast to 'integer?', which also returns true for inexact

> check-stx-keyword : symbol any -> void or raises error
> check-stx-string : symbol any -> void or raises error
> check-stx-bytes : symbol any -> void or raises error
> check-stx-vector : symbol any -> void or raises error
> check-stx-number : symbol any -> void or raises error
> check-stx-exact : symbol any -> void or raises error
> check-stx-inexact : symbol any -> void or raises error
> check-stx-rational : symbol any -> void or raises error
> check-stx-real : symbol any -> void or raises error
> check-stx-integer : symbol any -> void or raises error
> check-stx-positive-integer : symbol any -> void or raises error
> check-stx-nonnegative-integer : symbol any -> void or raises error
> check-stx-boolean : symbol any -> void or raises error
> check-stx-true : symbol any -> void or raises error
> check-stx-false : symbol any -> void or raises error
> check-stx-nonfalse : symbol any -> void or raises error

  The check procedures raise an error if the given value is not a
  syntax object containing the expected kind of data. The symbol is
  used as the first argument to 'raise-syntax-error'.

> stx-keyword-value : syntax -> keyword
> stx-string-value : syntax -> string
> stx-bytes-value : syntax -> bytes
> stx-number-value : syntax -> number
> stx-boolean-value syntax -> boolean

  These procedures check that their argument is a syntax object
  containing the expected kind of data and extract that data.

    (stx-string-value #'"here") = "here"
    (stx-number-value #'5) = 5
    (stx-number-value #'x) raises exception

  These procedures are equivalent to syntax-e plus error-checking.

> type symbollike = symbol | string | keyword | identifier

> symbol-append : symbollike ... -> symbol

  This procedure takes any number of symbol-like values (symbols,
  identifiers, and strings) and concatenates them, returning the
  result as a symbol.

    (sym+ #'name '- #'field) = 'name-field

> literal-identifier=? : identifier identifier -> boolean

  Returns true if the symbols contained in the two identifiers are
  the same.

> syntax-matches-pattern SYNTAX

  (syntax-matches-pattern stx keywords pattern1 pattern2 ...)

  Returns true if the syntax object 'stx' matches any of the
  patterns; returns false otherwise.

> with-syntax* SYNTAX

  (with-syntax* ([pattern expr] ...) . body)

  Like with-syntax, but the scope of pattern variables includes the
  right-hand sides of subsequent bindings.

    (with-syntax* ([(temp ...) (generate-temporaries the-variables)]
                   [(index ...) (iota (length (syntax->list the-variables)))]
                   [(index-binding ...) #'([temp index] ...)])

_qd.ss_: "Quick and Dirty" inspection

Imported with:
  (require (planet "" ("ryanc" "macros.plt" 1)))

The "" module provides special forms useful for interactive
debugging of syntax bindings, such as those introduced by
define-struct. Please don't use this library in real code.

> syntax-local-value/quote SYNTAX
> syntax-local-value/quote-syntax SYNTAX

  These forms take an identifier bound by define-syntax or
  let-syntax, look up its value in the syntactic environment, and
  return it as a runtime expression as an s-expression or as a syntax

    (define-struct s (a b))
    (syntax-local-value/quote s)
      => a list of the predicate, accessors, etc
    (syntax-local-value/quote-syntax s)
      => similar, but retains extra syntax object information

> phase1-eval/quote SYNTAX
> phase1-eval/quote-syntax SYNTAX

  These forms simply evaluate their expression arguments at compile
  time (and in the compile-time environment), returning the result as
  an s-expression or as a syntax object.

    (define-for-syntax the-box (box 10))
    (phase1-eval/quote (begin0 (unbox the-box) (set-box! the-box 12)))
      => 10

> expand/stop SYNTAX

  Expands the first argument (the expression) with the stop list
  given by the second argument.

    (expand/stop (when (and 1 2) (go!)) (and))
    = #'(if (and 1 2) (begin (#%app (#%top . go!))))
      ; because 'when' expands to 'if', and the 'and' stops expansion
      ; of that subexpression, but the other subexpression gets fully
      ; expanded

> expand/stop-kernel SYNTAX

  Like expand/stop, but also includes the mzscheme core syntactic
  forms. The sequence of identifiers to stop on is optional.

    (expand/stop-kernel (when (and 1 2) (go!)))
    = (expand/stop-kernel (when (and 1 2) (go!)) ())
    = #'(if (and 1 2) (begin (go!)))
      ; because expansion stops on 'if'

  See also the procedures exported by (lib "" "macro-debugger").

_class-iop.ss_: Macros for interface-oriented class programming

Import with:
  (require (planet "" ("ryanc" "macros.plt" 1)))

The "" module is designed to work with the standard class
system from mzlib, (lib "").

It adds interface-checking variants of 'send' as well as machinery for
static interfaces and binding forms that help reduce checks.

> define-interface SYNTAX

  (define-interface name (method-name ...))

  Defines 'name' as a static interface containing the given method
  names. The name can also be used in any context that expects a
  standard dynamic interface (as produced by the standard 'interface'

    (define-interface stack<%> (empty? push pop))
    => #<struct:interface:stack<%>>

> define-static-interface SYNTAX

  (define-static-interface name expression (method-name ...))

  Defines a static interface binding that wraps the dynamic interface
  value resulting from the expression.

  Use this form only to wrap interfaces defined in other people's
  code, like so:

    (define-static-interface s-editor<%> editor<%>
      (add-canvas add-undo adjust-cursor _____))

  Use 'define-interface' when writing new code.

> send: SYNTAX

  (send: obj interface method argument ...)

  A variant of the 'send' special form that performs two checks:
    - the method being called is in the interface given
    - the object the method is being called on supports that (entire)
      interface (not just the particular method)

  When the interface is a static interface (defined by
  'define-interface'), the first check is done statically, rather than
  a run time.

  If a name is statically known to be bound to an instance of the
  interface, then the second check is not performed at run time. See
  the binding forms below.

> send*: SYNTAX
> send/apply: SYNTAX

  (send*: obj interface (method arg ...) ...)
  (send/apply: obj interface method arg ... arg-list)

  Like the standard 'send*' and 'send/apply' but with interface

> define: SYNTAX

  There are two variants:

  (define: name interface expression)

  Defines 'name' as the result the expression, but first checks that
  the result implements the given interface. The name is thereafter
  considered trusted.

  (define: (name argument ...) . body)
  where each argument is either an identifier or (identifier : interface)

  Defines a function with checked arguments; see 'lambda:' below.

> lambda: SYNTAX

  (lambda: (argument ...) . body)
  where each argument is either an identifier or (identifier : interface)

  Creates a function with (optionally) checked arguments. When the
  function is applied, each argument with an interface is checked,
  and thereafter those names are considered trusted.

> init: SYNTAX
> init-private: SYNTAX

  (init: (name interface) ...)
  (init-private: (name interface) ...)

  Like 'init' and 'init-private:' but the values are checked during
  initialization and the names are thereafter considered trusted.

  The is no 'field:' form, as fields are mutable from outside the
  class body, and the macro system is not powerful enough to enforce
  the necessary invariant.