4 More Topics to Cover

Basic macrology

A macro is a rewrite rule. It only applies to certain terms (no general rewriters like optimization). It only applies in certain context. Expansion is outward-in: discovery process.

Expressions have dynamic and static contexts.

Dynamic context: continuation marks, parameters, other state (order of evaluation, etc).

Static context: environment, syntax parameters. More exotic: phase, submodule.

Basic hygiene.

Basic pattern matching.

Misc advice:
  • Don’t use a macro when a function would suffice.

  • Not every macro needs to be written.

Intermediate Macrology: Specifying and Validating Syntax

syntax-parse, syntax specification (grammars), validation and error-checking

The structure of syntax. Think in terms of nonterminals. When writing functions on syntax, write one function per nonterminal.

Do functions on subterms first, then talk about moving code to stxclass attrs.

syntax ergonomics, eg positional forms vs keyords vs identifier-tagged subforms.

Ergonomics/conventions: Eval subexprs in original order. Do error checking of arguments in order, when possible.

special subforms (w/o extensibility)

syntax-case and error-checking (aside)

Note: this section might need forward references to Phases section (just point out compile-time vs run-time code, say Phases deals with in more detail later).

Intermediate Macrology: Syntax Objects

Below the pattern-matching abstractions: syntax-e, datum->syntax, and quote-syntax.

Rule: Never use syntax->datum on an expression or a term containing expressions.

Source information.

Syntax properties.

Intermediate Macrology: Phases

Compile-time helper functions, modules, begin-for-syntax, phases, require for-{syntax,template}.

The Why of Phases.

syntax templates, phases, env catalog

Intermediate Macrology: communication w/ syntax-local-value

Communication via syntax-local-value. - ordinary communication - extensible subforms

Intermediate Macrology: uncategorized

Racket language elements: expressions, definitions, module-form forms, etc. Modules and languages.

Communication via compile-time state (eg, identifier tables). Define vs attach.


Identifier comparisons: bound-id=?, free-id=? (and phases).

3d syntax.

Hygiene details: marks, renames, namespaces, gradual discovery.

Breaking hygiene: how to do it right, and alternatives.

How to test macros: Test run-time behavior, test binding, test syntax errors.

Monolithic vs microlithic macros. Trampoline style macros.

Applicable structs and macros that act as expressions as well as something else (eg struct names).

template tricks: template, ellipsis-escaping, etc

macro-defining macros, using make-X-transformer to prevent code explosion

Advanced Macrology

Head expansion.

Full expansion, analysis, and transformation (instrumentation). In macro w/ local-expand; in run-time tool w/ expand.

(refer back to monolithic/microlithic/trampoline)


Two ways of embedding language X in Racket:

  1. (Racket) expr ::= .... | X

    X forms are Racket macros. X grammar must not collide with Racket grammar.

  2. (Racket) expr ::= .... | (begin-X X)

    X forms are not Racket macros. X grammar can collide with Racket grammar. Can have separate begin-X forms for different interpretations/behaviors of X fragments (eg for regular expressions: match vs enumerate).

In (1), if X ::= .... | expr also, then it’s not a language, it’s probably just a library or a data type.

(1) works most naturally with compositional translations. (2) can handle non-compositional translation, but can also do compositional via trampoline style.

Can also have hybrid style (3?) where every X form, when used in Racket context, implicitly does begin-X with self inside.

Other languages notes:


namespace mangagement: subtracting Racket bindings, renaming, etc

custom macro expanders, syntax-local-value vs static