I am confused about the &environment parameter in common lisp. In particular, what is it useful for, and why is it a parameter, rather than a special variable?
EDIT: It would also be nice to see a concrete example of how &environment
would be used in code.
Macro Lambda Lists:
&environment
is followed by a single variable that is bound to an environment representing the lexical environment in which the macro call is to be interpreted. This environment should be used withmacro-function
,get-setf-expansion
,compiler-macro-function
, andmacroexpand
(for example) in computing the expansion of the macro, to ensure that any lexical bindings or definitions established in the compilation environment are taken into account.
Operators which define macros (local or global) have to define how code is expanded before being evaluation or compiled, and thus may need to expand existing macros, whose expansion may depend on the environment - so the macro definitions need the environment.
Special variables are more dangerous because the user might rebind them and because they are harder to handle correctly in multi-threaded code.
All over the places.lisp
:
(defmacro psetf (&whole whole-form
&rest args &environment env)
(labels ((recurse (args)
(multiple-value-bind (temps subforms stores setterform getterform)
(get-setf-expansion (car args) env)
(declare (ignore getterform))
(when (atom (cdr args))
(error-of-type 'source-program-error
:form whole-form
:detail whole-form
(TEXT "~S called with an odd number of arguments: ~S")
'psetf whole-form))
(wrap-let* (mapcar #'list temps subforms)
`(MULTIPLE-VALUE-BIND ,stores ,(second args)
,@(when (cddr args) (list (recurse (cddr args))))
,@(devalue-form setterform))))))
(when args `(,@(recurse args) NIL))))
From iolib/src/new-cl/definitions.lisp
:
(defmacro defconstant (name value &optional documentation
&environment env)
(destructuring-bind (name &key (test ''eql))
(alexandria:ensure-list name)
(macroexpand-1
`(alexandria:define-constant ,name ,value
:test ,test
,@(when documentation `(:documentation ,documentation)))
env)))
For example when Lisp wants to macroexpand a form, it needs to know which name refers to what macro definition. This can depend on the globally available macros, locally bound macros via macrolet
or what is available during compilation. Thus the environment object makes it possible to macroexpand a form with respect to different environments.
Environment variables provide to the macro author information about the declarations et. al. enforce when the macro was invoked. The macro author can then use that information to customize how he chooses to expand the macro. For example he might add or subtract debugging code based on declarations he finds in the environment. For example he might infer the type of expressions he was given to work with and add or subtract code to get better performance.
You can write a lot of Common Lisp code and never use &environment. There are situations where it becomes necessary, when those are would make good follow up question. Rainer hints at an answer to that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With