Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of &environment in Common Lisp?

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.

like image 519
Thayne Avatar asked May 29 '14 23:05

Thayne


3 Answers

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)))
like image 135
sds Avatar answered Oct 15 '22 02:10

sds


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.

like image 33
Rainer Joswig Avatar answered Oct 15 '22 02:10

Rainer Joswig


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.

like image 23
Ben Hyde Avatar answered Oct 15 '22 02:10

Ben Hyde