What is the purpose of &environment in Common Lisp?
Asked Answered
A

3

7

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.

Ard answered 29/5, 2014 at 23:46 Comment(0)
A
4

Doc

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 with macro-function, get-setf-expansion, compiler-macro-function, and macroexpand (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.

Explanation

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.

Not a Special Variable

Special variables are more dangerous because the user might rebind them and because they are harder to handle correctly in multi-threaded code.

Example

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)))
Aright answered 30/5, 2014 at 3:10 Comment(0)
M
4

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.

Mcclimans answered 30/5, 2014 at 7:3 Comment(1)
This is a useful description, thank you, @Rainer. Perhaps it could be further improved with some example code, though? I'm still unclear on how this would be used. (@sds, perhaps your answer would benefit similarly.)Beora
F
2

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.

Fendig answered 1/6, 2014 at 0:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.