== name == Environments Access == category == Program Analysis == author == Duane Rettig (mailto(duane@franz.com), mailto(support@franz.com)); adapted from Common Lisp the Language, second edition, pp 207-214. == author-image == == short-description == Environments are essential to the correct compilation and evaluation of Common Lisp forms and programs. However, environments are not first-class, in the sense that they cannot be easily examined and manipulated as can most other Common Lisp objects. The Ansi X3J13 committee recognized this need, and attempted to define accessors and constructors for environment objects, but decided not to standardize them due to semantic difficulties. At Franz Inc, we have created "Environments Access", a potential portable reference implementation of this functionality. == long-description == An environment in Common Lisp is a Lisp object that contains, in some fashion, a set of bindings and also information about operators, variables, symbols, and so on. This information can be useful in various ways, particularly during evaluation, compilation and macro expansion. The macroexpand operator takes an optional environment argument, whose value is either nil or an environment object. The actual nature of an environment object is implementation dependent. In standard Common Lisp, there is no way for programmers to access or modify environment objects. In the proposed Common Lisp standard specified by Guy Steele et. al. in the second edition of Common Lisp: the Language, an interface to environment objects was proposed (in Section 8.5, pp 207-214), but the ANSI J13 committee decided not to include this in the ANS. Allegro CL now has implemented much of the CLtL-2 environments proposal, with some differences, and that implementation has now been made Open Source as this Environments Access module, so that other implementations can make use of the implementation as well, and so that Environments can potentially be accessed portably. For documentation, we recommend that users read the section of CLtL-2 described above, although href(this document, http://www.franz.com/support/documentation/7.0/doc/environments.htm) is complete in itself. Also available is a href(Powerpoint presentation,/source/envaccess/envaccess.ppt) with notes included, about this module. Note that due to some last minute changes after Allegro CL 7.0 was released, the Environments Access module is not compatible with 7.0, and can't be loaded into Allegro CL. However, the module is already loaded into Allegro CL 7.0, and the changes are noted in the Powerpoint presentation, so users of Allegro CL can just start using it. == examples == 1. Basic function call to determine what declarations are available In cmucl: * (pprint (sys::declaration-information 'declaration)) (SYSTEM::VARIABLE-REBINDING DYNAMIC-EXTENT SPECIAL IGNORABLE IGNORE NOTINLINE INLINE FTYPE SYSTEM::DISCRIMINATED-TYPE TYPE DECLARATION OPTIMIZE) * In Allegro CL: CL-USER(1): (pprint (sys::declaration-information 'declaration)) (:FASLMODE VALUES COMPILER::INSTANCE-VARIABLE :EXPLAIN EXCL::DYNAMIC-EXTENT-ARGUMENTS :DISCARD-SOURCE-FILE-INFO SYSTEM::VARIABLE-REBINDING DYNAMIC-EXTENT SPECIAL IGNORE-IF-UNUSED IGNORABLE IGNORE NOTINLINE INLINE FTYPE SYSTEM::DISCRIMINATED-TYPE TYPE DECLARATION OPTIMIZE) CL-USER(2): 2. Some experiments on the optimize declaration: In CMUCL: * (setq e1 (sys::make-compilation-unit-environment)) Warning: Declaring E1 special. # * (setq e2 (sys::augment-environment e1 :declare '((optimize speed debug)))) Warning: Declaring E2 special. # * (sys::declaration-information 'optimize) ((SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) Note that the information is pushed, above, and does not modify the oiginal data here. * (sys::declaration-information 'optimize e2) ((SPEED 3) (DEBUG 3) (SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) * (declaim (optimize (space 0) (safety 0))) NIL * (sys::declaration-information 'optimize) ((SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) * Note that the above did not make any difference, because Environments Access is currently implemented in CMUCL at level 1, and thus declaim does not invoke augment-environment (a level 2 acivity). Contrast with the Allegro CL example below. * (sys::augment-environment nil :declare '((optimize (space 0) (safety 0)))) NIL * (sys::declaration-information 'optimize) ((SPACE 0) (SAFETY 0) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) note now that the values have actually changed, but... * (sys::declaration-information 'optimize e2) ((SPEED 3) (DEBUG 3) (SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) * Note also that the global change did not affect anything in this local environment. Now in Allegro CL: CL-USER(3): (setq e1 (sys::make-compilation-unit-environment)) # CL-USER(4): (setq e2 (sys::augment-environment e1 :declare '((optimize speed debug)))) # CL-USER(5): (sys::declaration-information 'optimize) ((SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) CL-USER(6): (sys::declaration-information 'optimize e2) ((SPEED 3) (DEBUG 3) (SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) CL-USER(7): (declaim (optimize (space 0) (safety 0))) T CL-USER(8): (sys::declaration-information 'optimize) ((SPACE 0) (SAFETY 0) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) Note above that the declaim took effect, and affected the global optimize namespace. If we had been in a compile-file, the shadow environment's optimize namespace would have been affected and not the global space. CL-USER(9): (sys::declaration-information 'optimize e2) ((SPEED 3) (DEBUG 3) (SAFETY 1) (SPACE 1) (SPEED 1) (COMPILATION-SPEED 1) (DEBUG 2)) CL-USER(10): 3. Peter Seibel complains in his book (Practical Common Lisp, p 441) that there is no way to call eval with an explicit environment, and he develops a workaround. Well, in Allegro CL, and this module, w can do just that: CL-USER(2): (compile (defun eval-in-env (form sys::*interpreter-environment*) (excl::%eval form))) EVAL-IN-ENV NIL NIL CL-USER(3): (setq e1 (sys::augment-environment (sys::make-augmentable-environment-boa :interpreter) :variable 'x :locative (list 10))) # CL-USER(4): (describe 'x) X is a NEW SYMBOL. It is unbound. It is INTERNAL in the COMMON-LISP-USER package. CL-USER(5): (sys:variable-information 'x) NIL CL-USER(6): (sys:variable-information 'x e1) :LEXICAL (10) NIL T CL-USER(7): (eval 'x) Error: Attempt to take the value of the unbound variable `X'. [condition type: UNBOUND-VARIABLE] Restart actions (select using :continue): 0: Try evaluating X again. 1: Use :X instead. 2: Set the symbol-value of X and use its value. 3: Use a value without setting X. 4: Return to Top Level (an "abort" restart). 5: Abort entirely from this (lisp) process. [1] CL-USER(8): :res CL-USER(9): (eval-in-env 'x e1) 10 CL-USER(10): (eval-in-env '(setq x 1) e1) 1 CL-USER(11): (sys:variable-information 'x e1) :LEXICAL (1) NIL T CL-USER(12): 4. During compilation, variable-information and function-information can tell us things about the names being processed. Note that this will only work on a level 4 Implementation (currently only Allegro CL). Note also that the values of variable and function information slots during compilation are implementation-dependent. (href(vifitest.cl,/source/envaccess/vifitest.cl) is needed for this next example.) CL-USER(1): (shell "cat vifitest.cl") (in-package :user) ;; Avoid infinite recursion: (eval-when (compile) (without-package-locks comp::(defmethod print-object ((x varrec) s) (print-unreadable-object (x s :type t :identity nil) (format s "~s~@[ ~:_Specialp: ~a~]~@[ ~:_~a~]" (varrec-name x) (varrec-specialp x) (varrec-type x)))))) (defmacro with-vi-test ((var &optional all) &body body &environment e) (format t "variable-information ~:[~;(all)~] of ~s is ~s~%" all var (multiple-value-list (sys:variable-information var e all))) `(progn ,@body)) (defmacro with-fi-test ((var &optional all spec) &body body &environment e) (format t "function-information ~:[~;(all)~]~:[~; w/spec~] of ~s is ~s~%" all spec var (multiple-value-list (sys:function-information var e all spec))) `(progn ,@body)) (defconstant seven 7) (defvar *framis*) (defmacro not-twelve (x) `(/= 12 ,x)) (define-symbol-macro not-eleven (1- seven)) (let ((x (multiple-value-list (get-universal-time)))) (declare (special frob)) (symbol-macrolet ((pie 22/7)) (flet ((epi (x) (1+ x))) (macrolet ((poi (x) `(sqrt ,x))) (defun load-time () (with-vi-test (x) (with-vi-test (frob) (with-vi-test (*print-base*) (with-vi-test (seven) (with-vi-test (not-eleven) (with-vi-test (pi) (with-vi-test (pi t) (with-vi-test (*framis*) (let ((*framis* 10.0)) (with-vi-test (*framis*) (with-vi-test (unknown) (+ seven x))))))))))))) (defun apple-pie (apple) (with-vi-test (apple) (with-vi-test (pie) (with-fi-test (or) (with-fi-test (or t) (with-fi-test (or t t) (with-fi-test (poi) (with-fi-test (not-twelve) (with-fi-test (car) (with-fi-test (car t) (with-fi-test (epi) (print (values pi (* apple pie)))))))))))))))))) 0 CL-USER(2): :cf vifitest ;;; Compiling file vifitest.cl variable-information of X is (:LEXICAL (#) NIL :OUTER) variable-information of FROB is (:SPECIAL (#) NIL :OUTER) variable-information of *PRINT-BASE* is (:SPECIAL NIL NIL) variable-information of SEVEN is (:CONSTANT 7 NIL) variable-information of NOT-ELEVEN is (:SYMBOL-MACRO ((1- SEVEN)) NIL) variable-information of PI is (:CONSTANT 3.141592653589793d0 NIL) variable-information (all) of PI is (:CONSTANT 3.141592653589793d0 ((TYPE (DOUBLE-FLOAT 3.141592653589793d0 3.141592653589793d0)))) variable-information of *FRAMIS* is (:SPECIAL NIL NIL) variable-information of *FRAMIS* is (:SPECIAL (#) NIL :OUTER) variable-information of UNKNOWN is (NIL) variable-information of APPLE is (:LEXICAL (#) NIL :OUTER) variable-information of PIE is (:SYMBOL-MACRO (22/7) NIL :OUTER) function-information of OR is (:MACRO NIL NIL) function-information (all) of OR is (:MACRO (#) NIL) function-information (all) w/spec of OR is (:SPECIAL-OPERATOR NIL NIL) function-information of POI is (:MACRO (#) NIL T) function-information of NOT-TWELVE is (:MACRO NIL NIL) function-information of CAR is (:FUNCTION NIL NIL) function-information (all) of CAR is (:FUNCTION (#) NIL) function-information of EPI is (:FUNCTION (#:G45) NIL T) ;;; Writing fasl file vifitest.fasl ;;; Fasl write complete CL-USER(3): == instructions == Allegro CL 7.0 users should not try to load this module; it is already loaded into every Allegro CL 7.0 lisp, and there are some incompatibilities that force an error to be signalled if an attempt is made to load the files. Non-Allegro CL users should first compile and load environ.cl, followed by compiling and loading defdecl.cl. == tutorial == The href(ILC 2005 Powerpoint presentation,/source/envaccess/envaccess.ppt) is a good tutorial. == home-url == == doc-url == http://www.franz.com/support/documentation/7.0/doc/environments.htm == license == href(LLGPL,http://opensource.franz.com/preamble.html) == book == == references == == source-fooball == href(environ.cl,/source/envaccess/environ.cl) and href(defdecl.cl,/source/envaccess/defdecl.cl) make up the implementation. href(vifitest.cl,/source/envaccess/vifitest.cl) is part of the examples. == release-date == 2005-06-17 == release-version == 7.1.pre-beta == status == beta == history == A version of this module has been incorporated into Allegro CL since version 6.1, where it became the default underlying implementation for the interpreter. As of version 7.0, it has also become the underlying implementation for the compiler. == acl-dependencies == This module does not load into Allegro CL 7.0, due to some minor incompatibilities. But since every Allegro CL 7.0 lisp has a version of the module already loaded, the module can be used without having to install it. == other-dependencies == == platform == This module is intended to work with all major CL implementations at a "level 1" state (see the href(Powerpoint presentation,/source/envaccess/envaccess.ppt)), and is known to work on CMUCL (18e and 19a), clisp (2.33.2), and SBCL (0.8.16). It compiles and loads on an old 4.1 version of Lispworks, but environment objects cannot be printed; a format bug is suspected. Later versions of LW should work, but we have no way of testing it. Please feed back version info if you find later versions on which it works. Fixes for making the modules work in any lisp are welcome. == ad ==