The macrodynamics Reference Manual

Table of Contents

Next: , Previous: , Up: (dir)   [Contents][Index]

The macrodynamics Reference Manual

This is the macrodynamics Reference Manual, version 0.1.1, generated automatically by Declt version 2.4 "Will Decker" on Wed Jun 20 12:13:19 2018 GMT+0.


Next: , Previous: , Up: Top   [Contents][Index]

1 Introduction

Macrodynamics

Build Status Quicklisp

Macrodynamics is a language extension that broadens the notion of dynamic scope inside macroexpansion code. Macrodynamic bindings are scoped not just to the code called during an individual expansion, but also to subsequent expansions of the code returned within the dynamic scope of those bindings.

This goes a long way toward rectifying a major limitation of Common Lisp, described in detail here. In short, you can write macros that behave as if they were expanded recursively instead of iteratively. However, macrodynamics only lets data flow downstream from where it is bound; it does not provide analogues of conditions or continuations to transmit information back up the expansion stack.

Usage

You can declare macrodynamic variables with def-dynenv-var, a cognate of defvar. These variables can be left unbound or given a top-level, global value.

(def-dynenv-var **my-macrodynamic-var** value)
(def-dynenv-var **my-unbound-macrodynamic-var**)

(You may want to use a special notational convention for macrodynamic variables and functions. I prefer **double-earmuffs** myself.)

For macrodynamic functions, def-dynenv-fun works like defun, while def-unbound-dynenv-fun only takes a name which remains unbound at the top-level. (There's just no way to sensibly combine both syntaxes within one macro.)

(def-dynenv-fun **my-macrodynamic-fun** (&rest stuff) (do-stuff-with stuff))
(def-unbound-dynenv-fun **my-unbound-macrodynamic-fun**)

Macrodynamic variables and functions live in a separate namespace from regular Lisp variables (whether lexical or dynamic) and functions. To establish bindings for them, you must use one of a few dynamic-only compile-time variants of familiar operators. ct-let and ct-let* bind variables, and ct-flet binds functions.

Within the body of any function definition created with ct-flet, the function name call-next-dynenv-fun is bound (lexically, not dynamically!) to the previously dynamically-bound function with the same name. But any recursive invocation of the function by name, even within a call to call-next-dynenv-fun, will always invoke the most recently dynamically-bound function. You can also retrieve dynamically-bound functions as values using dynenv-function instead of function or #'. function/#' simply retrieves a wrapper for calling the dynamically-bound function, which may be rebound between the time you retrieve the wrapper and the time you invoke it. For more details, see Pascal Costanza's paper on dynamically-scoped functions.

To define macros that need to read or bind macrodynamic entities within the dynamic scope of their expander code, you can use def-dynenv-macro:

(def-dynenv-var **a-macrodynamic-var** nil)

(def-dynenv-macro some-macro (&body body)
  `(do-stuff
       ,(do-something)
     ,(ct-let ((**a-macrodynamic-var**
                (non-destructively-augment **a-macrodynamic-var**)))
        `(progn ,@body))))

;; this function will signal an error if not called within the dynamic scope
;; of a macrodynamic macro's expansion
(defun do-something ()
  (generate-code-with **a-macrodynamic-var**))

(defmacro dummy-wrapper-for-some-macro (&body body)
  `(some-macro ,@body))

Then if you write a form like this:

(some-macro
  (dummy-wrapper-for-some-macro
    (some-macro
      (some-other-code))))

do-something will see the global binding nil for **a-macrodynamic-var** during the expansion of the top-level some-macro form, then it will see a new binding equivalent to (non-destructively-augment nil) in the expansion of the some-macro form that dummy-wrapper-for-some-macro's expansion returns, then another new binding equivalent to (non-destructively-augment (non-destructively-augment nil)) in the innermost some-macro expansion.

def-dynenv-macro is just a convenience macro that can extract the macrodynamic context from the lexical environment regardless of whether you include an &environment parameter. (An analogous dynenv-macrolet macro is also provided.) Alternatively, you can explicitly pass an environment to with-dynenv at the top of a macro's body (or at least surrounding any forms that need to bind or reference macrodynamic entities). This makes it easier to integrate macrodynamics with any other special macro-defining-macros you might want to use.

(def-macro-using-some-other-macro-library some-macro
    (&body body &environment env)
  (with-dynenv env
    `(do-stuff
         ,(do-something)
       ,(ct-let ((**a-macrodynamic-var**
                  (non-destructively-augment **a-macrodynamic-var**)))
          (remember-that-you-can-also-see-the-new-value-of **a-macrodynamic-var**
             immediately-right-here-in-the-same-expansion-step!
             `(progn ,@body))))))

This library is meant to be used in a purely functional manner, and it will signal an error if you attempt to set, rather than rebind, a macrodynamic binding. That's right, dynamic scope is compatible with functional programming; it just admits a slightly looser definition of referential transparency. You can think of dynamic variables as an implicit set of additional arguments passed to every function. When dynamic bindings are in play, a function called with the same arguments may not always return the same results, but a function called at the top-level with the same arguments always will. What this means for macrodynamics is that an entire top-level form will always have the same macroexpansion. Normally, this is all you really care about, since you spend most of your time reasoning about top-level forms that you can see in their entirety.

One drawback is that you won't always be able to use SLIME's C-c C-m to verify what a non-top-level expression will expand into, but this is no different from any other situation in which you might use macrolet or symbol-macrolet. Macrodynamics are no more dangerous than lexically-bound macros; in fact, they're just an abstraction layer built on top of symbol-macrolet.

But Why?

What is dynamic from the perspective of expander code is lexical from the perspective of expanded code. When you take full advantage of this semantic duality, it's easy to lexically scope, and thus make more composable, certain implementation concerns that do not easily map onto lexical runtime variables. In the most common case, you can bind compile-time metadata about how a given variable is meant to be interpreted by another macro that may or may not be used further down the syntax tree.

Instead of placing your trust in a code walker like macroexpand-dammit, macrodynamics piggybacks on your implementation's built-in macroexpansion facility. When you use a code walker, you introduce a potential point of failure that can screw up the expansion of code in between the macro that establishes a compile-time dynamic context and the macro that uses it. With macrodynamics, you can trust your Lisp implementation to correctly process any in-between special operators, function calls, and macros that were written without any knowledge of this language extension.


Next: , Previous: , Up: Top   [Contents][Index]

2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 macrodynamics

Author

Kyle Littler

Home Page

https://github.com/DalekBaldwin/macrodynamics

License

LLGPL

Description

A language extension for creating bindings scoped to the entire expansion process of a region of code.

Version

0.1.1

Dependency

alexandria

Source

macrodynamics.asd (file)

Components

Next: , Previous: , Up: Top   [Contents][Index]

3 Modules

Modules are listed depth-first from the system components tree.


Previous: , Up: Modules   [Contents][Index]

3.1 macrodynamics/src

Dependency

macrodynamics.asd (file)

Parent

macrodynamics (system)

Location

src/

Components

Next: , Previous: , Up: Top   [Contents][Index]

4 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Next: , Previous: , Up: Files   [Contents][Index]

4.1 Lisp


Next: , Previous: , Up: Lisp files   [Contents][Index]

4.1.1 macrodynamics.asd

Location

macrodynamics.asd

Systems

macrodynamics (system)


Next: , Previous: , Up: Lisp files   [Contents][Index]

4.1.2 macrodynamics/src/package.lisp

Parent

src (module)

Location

src/package.lisp

Packages

macrodynamics

Internal Definitions

*system-directory* (special variable)


Next: , Previous: , Up: Lisp files   [Contents][Index]

4.1.3 macrodynamics/src/util.lisp

Dependency

package.lisp (file)

Parent

src (module)

Location

src/util.lisp

Internal Definitions

Previous: , Up: Lisp files   [Contents][Index]

4.1.4 macrodynamics/src/macrodynamics.lisp

Dependency

util.lisp (file)

Parent

src (module)

Location

src/macrodynamics.lisp

Exported Definitions
Internal Definitions

Previous: , Up: Files   [Contents][Index]

4.2 Other


Previous: , Up: Other files   [Contents][Index]

4.2.1 macrodynamics/macrodynamics.asd

Parent

macrodynamics (system)

Location

macrodynamics.asd


Next: , Previous: , Up: Top   [Contents][Index]

5 Packages

Packages are listed by definition order.


Previous: , Up: Packages   [Contents][Index]

5.1 macrodynamics

Source

package.lisp (file)

Use List
Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

6 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

6.1 Exported definitions


Next: , Previous: , Up: Exported definitions   [Contents][Index]

6.1.1 Macros

Macro: ct-flet DEFINITIONS &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: ct-let BINDINGS &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: ct-let* BINDINGS &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: def-dynenv-fun NAME ARGS &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: def-dynenv-macro NAME LAMBDA-LIST &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: def-dynenv-var VAR &optional VAL
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: def-unbound-dynenv-fun NAME
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: dynenv-function NAME
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: dynenv-macrolet DEFINITIONS &body BODY
Package

macrodynamics

Source

macrodynamics.lisp (file)

Macro: with-dynenv ENVIRONMENT &body BODY

Macro for capturing a dynenv within another macro’s body.

Package

macrodynamics

Source

macrodynamics.lisp (file)


Previous: , Up: Exported definitions   [Contents][Index]

6.1.2 Conditions

Condition: unbound-dynenv-macro-fun ()
Package

macrodynamics

Source

macrodynamics.lisp (file)

Direct superclasses

condition (condition)

Direct methods
  • fun (method)
  • fun (method)
Direct slots
Slot: fun
Initargs

:fun

Readers

fun (generic function)

Writers

(setf fun) (generic function)

Condition: unbound-dynenv-macro-var ()
Package

macrodynamics

Source

macrodynamics.lisp (file)

Direct superclasses

condition (condition)

Direct methods
  • var (method)
  • var (method)
Direct slots
Slot: var
Initargs

:var

Readers

var (generic function)

Writers

(setf var) (generic function)


Previous: , Up: Definitions   [Contents][Index]

6.2 Internal definitions


Next: , Previous: , Up: Internal definitions   [Contents][Index]

6.2.1 Special variables

Special Variable: *eval-phases*
Package

macrodynamics

Source

macrodynamics.lisp (file)

Special Variable: *fun-space*
Package

macrodynamics

Source

macrodynamics.lisp (file)

Special Variable: *system-directory*
Package

macrodynamics

Source

package.lisp (file)

Special Variable: *unbound*
Package

macrodynamics

Source

macrodynamics.lisp (file)

Special Variable: *var-space*
Package

macrodynamics

Source

macrodynamics.lisp (file)

Special Variable: *within-captured-dynenv*
Package

macrodynamics

Source

macrodynamics.lisp (file)


Next: , Previous: , Up: Internal definitions   [Contents][Index]

6.2.2 Symbol macros

Symbol Macro: -unbound-
Package

macrodynamics

Source

macrodynamics.lisp (file)

Expansion

(load-time-value macrodynamics::*unbound*)


Next: , Previous: , Up: Internal definitions   [Contents][Index]

6.2.3 Functions

Function: dynenv-function% SYMBOL
Package

macrodynamics

Source

macrodynamics.lisp (file)

Function: get-assoc ITEM ALIST &rest KEYS &key KEY TEST TEST-NOT

Like ASSOC but returns the cdr instead of the whole matching cons and a second value indicating success or failure.

Package

macrodynamics

Source

util.lisp (file)

Function: get-dynenv-var VAR
Function: (setf get-dynenv-var) VAL VAR
Package

macrodynamics

Source

macrodynamics.lisp (file)

Function: update-alist ITEM VALUE ALIST

Non-destructively replace cdr of the cons whose car matches ITEM in ALIST with VALUE, or insert a new cons if no car matches ITEM.

Package

macrodynamics

Source

util.lisp (file)


Previous: , Up: Internal definitions   [Contents][Index]

6.2.4 Generic functions

Generic Function: fun CONDITION
Generic Function: (setf fun) NEW-VALUE CONDITION
Package

macrodynamics

Methods
Method: fun (CONDITION unbound-dynenv-macro-fun)
Method: (setf fun) NEW-VALUE (CONDITION unbound-dynenv-macro-fun)
Source

macrodynamics.lisp (file)

Generic Function: var CONDITION
Generic Function: (setf var) NEW-VALUE CONDITION
Package

macrodynamics

Methods
Method: var (CONDITION unbound-dynenv-macro-var)
Method: (setf var) NEW-VALUE (CONDITION unbound-dynenv-macro-var)
Source

macrodynamics.lisp (file)


Previous: , Up: Top   [Contents][Index]

Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts

Jump to:   F   L   M   O  
Index Entry  Section

F
File, Lisp, macrodynamics.asd: The macrodynamics<dot>asd file
File, Lisp, macrodynamics/src/macrodynamics.lisp: The macrodynamics/src/macrodynamics<dot>lisp file
File, Lisp, macrodynamics/src/package.lisp: The macrodynamics/src/package<dot>lisp file
File, Lisp, macrodynamics/src/util.lisp: The macrodynamics/src/util<dot>lisp file
File, other, macrodynamics/macrodynamics.asd: The macrodynamics/macrodynamics<dot>asd file

L
Lisp File, macrodynamics.asd: The macrodynamics<dot>asd file
Lisp File, macrodynamics/src/macrodynamics.lisp: The macrodynamics/src/macrodynamics<dot>lisp file
Lisp File, macrodynamics/src/package.lisp: The macrodynamics/src/package<dot>lisp file
Lisp File, macrodynamics/src/util.lisp: The macrodynamics/src/util<dot>lisp file

M
macrodynamics.asd: The macrodynamics<dot>asd file
macrodynamics/macrodynamics.asd: The macrodynamics/macrodynamics<dot>asd file
macrodynamics/src: The macrodynamics/src module
macrodynamics/src/macrodynamics.lisp: The macrodynamics/src/macrodynamics<dot>lisp file
macrodynamics/src/package.lisp: The macrodynamics/src/package<dot>lisp file
macrodynamics/src/util.lisp: The macrodynamics/src/util<dot>lisp file
Module, macrodynamics/src: The macrodynamics/src module

O
Other File, macrodynamics/macrodynamics.asd: The macrodynamics/macrodynamics<dot>asd file

Jump to:   F   L   M   O  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   (  
C   D   F   G   M   U   V   W  
Index Entry  Section

(
(setf fun): Internal generic functions
(setf fun): Internal generic functions
(setf get-dynenv-var): Internal functions
(setf var): Internal generic functions
(setf var): Internal generic functions

C
ct-flet: Exported macros
ct-let: Exported macros
ct-let*: Exported macros

D
def-dynenv-fun: Exported macros
def-dynenv-macro: Exported macros
def-dynenv-var: Exported macros
def-unbound-dynenv-fun: Exported macros
dynenv-function: Exported macros
dynenv-function%: Internal functions
dynenv-macrolet: Exported macros

F
fun: Internal generic functions
fun: Internal generic functions
Function, (setf get-dynenv-var): Internal functions
Function, dynenv-function%: Internal functions
Function, get-assoc: Internal functions
Function, get-dynenv-var: Internal functions
Function, update-alist: Internal functions

G
Generic Function, (setf fun): Internal generic functions
Generic Function, (setf var): Internal generic functions
Generic Function, fun: Internal generic functions
Generic Function, var: Internal generic functions
get-assoc: Internal functions
get-dynenv-var: Internal functions

M
Macro, ct-flet: Exported macros
Macro, ct-let: Exported macros
Macro, ct-let*: Exported macros
Macro, def-dynenv-fun: Exported macros
Macro, def-dynenv-macro: Exported macros
Macro, def-dynenv-var: Exported macros
Macro, def-unbound-dynenv-fun: Exported macros
Macro, dynenv-function: Exported macros
Macro, dynenv-macrolet: Exported macros
Macro, with-dynenv: Exported macros
Method, (setf fun): Internal generic functions
Method, (setf var): Internal generic functions
Method, fun: Internal generic functions
Method, var: Internal generic functions

U
update-alist: Internal functions

V
var: Internal generic functions
var: Internal generic functions

W
with-dynenv: Exported macros

Jump to:   (  
C   D   F   G   M   U   V   W  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.3 Variables

Jump to:   *   -  
F   S   V  
Index Entry  Section

*
*eval-phases*: Internal special variables
*fun-space*: Internal special variables
*system-directory*: Internal special variables
*unbound*: Internal special variables
*var-space*: Internal special variables
*within-captured-dynenv*: Internal special variables

-
-unbound-: Internal symbol macros

F
fun: Exported conditions

S
Slot, fun: Exported conditions
Slot, var: Exported conditions
Special Variable, *eval-phases*: Internal special variables
Special Variable, *fun-space*: Internal special variables
Special Variable, *system-directory*: Internal special variables
Special Variable, *unbound*: Internal special variables
Special Variable, *var-space*: Internal special variables
Special Variable, *within-captured-dynenv*: Internal special variables
Symbol Macro, -unbound-: Internal symbol macros

V
var: Exported conditions

Jump to:   *   -  
F   S   V  

Previous: , Up: Indexes   [Contents][Index]

A.4 Data types

Jump to:   C   M   P   S   U  
Index Entry  Section

C
Condition, unbound-dynenv-macro-fun: Exported conditions
Condition, unbound-dynenv-macro-var: Exported conditions

M
macrodynamics: The macrodynamics system
macrodynamics: The macrodynamics package

P
Package, macrodynamics: The macrodynamics package

S
System, macrodynamics: The macrodynamics system

U
unbound-dynenv-macro-fun: Exported conditions
unbound-dynenv-macro-var: Exported conditions

Jump to:   C   M   P   S   U