Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Common Lisp, how to test if variable is special?

Tags:

common-lisp

I thought I would be able to find this through Google, SO, or the books I'm reading, but it is proving elusive.

In the implementation I'm learning with, I can do the following at the top-level:

(defvar *foo* 4) (set 'bar 3)

If I then call (describe '*foo*) and (describe 'bar), I get a description saying that *foo* is special and bar is non-special (among other details).

Is there a function that takes a symbol variable as an argument and returns true or false if it is special? If so, is describe probably implemented in part by calling it?

Context: I'm learning Common Lisp, but at work I have a system with a dialect of Lisp similar to Common Lisp, but the describe function is unimplemented. There's sort of an XY thing going on here, but I'm also trying to grok Lisp and CL.

like image 802
brian_o Avatar asked Feb 04 '15 17:02

brian_o


People also ask

How do you define a variable in a Common Lisp?

Common Lisp provides two ways to create global variables: DEFVAR and DEFPARAMETER . Both forms take a variable name, an initial value, and an optional documentation string. After it has been DEFVAR ed or DEFPARAMETER ed, the name can be used anywhere to refer to the current binding of the global variable.

Do VS do * In LISP?

As each step-form is evaluated, the corresponding variable is assigned. Therefore under do , when the step-form refers to any variable, it refers to its value from the prior iteration. Under do* , if the step-form refers to a lexically earlier variable, it's picking up the new value.

What does Setf mean in Common Lisp?

setf is actually a macro that examines an access form and produces a call to the corresponding update function. Given the existence of setf in Common Lisp, it is not necessary to have setq, rplaca, and set; they are redundant. They are retained in Common Lisp because of their historical importance in Lisp.

How do you compare symbols in a LISP?

To compare the characters of two strings, one should use equal, equalp, string=, or string-equal. Compatibility note: The Common Lisp function eql is similar to the Interlisp function eqp.


2 Answers

Many Common Lisp implementations provide the function variable-information in some system dependent package.

Here in SBCL:

* (require :sb-cltl2)
NIL

* (sb-cltl2:variable-information '*standard-output*)
:SPECIAL
NIL
((TYPE . STREAM))

This function was proposed as part of some other functionality to be included into ANSI CL, but didn't make it into the standard. Still many implementations have it. For documentation see: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node102.html

like image 88
Rainer Joswig Avatar answered Oct 10 '22 03:10

Rainer Joswig


A non-special variable's environment will be captured when you create a closure over it:

(let ((x 1))
  (let ((f (lambda () x)))
    (let ((x 2))
      (eql 2 (funcall f)))))
;;=> NIL

A special variable's lexical environment will not:

(defvar *x*) ; *x* is special

(let ((*x* 1))
  (let ((f (lambda () *x*)))
    (let ((*x* 2))
      (eql 2 (funcall f)))))
;;=> T

Using this approach, you could easily define a macro that will expand to code like the previous that will let you determine whether a symbol is globally proclaimed special:

(defmacro specialp (symbol)
  (let ((f (gensym "FUNC-")))
    `(let ((,symbol 1))
       (let ((,f (lambda () ,symbol)))
         (let ((,symbol 2))
           (eql 2 (funcall ,f)))))))

(specialp x) ;=> NIL
(specialp *x*) ;=> T

Note that this isn't a function, it's a macro. That means that the macro function for specialp is getting called with the symbols X and *X*. This is important, because we have to construct code that uses these symbols. You can't do this with a function, because there'd be no (portable) way to take a symbol and create a lexical environment that has a lexical variable with that name and a lambda function that refers to it.

This also has some risks if you try to use it with certain symbols. For instance, in SBCL, if you try to bind, e.g., *standard-output* to something that isn't a stream or a stream designator, you'll get an error:

CL-USER> (specialp *standard-output*)
; in: SPECIALP *STANDARD-OUTPUT*
;     (LET ((*STANDARD-OUTPUT* 1))
;       (LET ((#:FUNC-1038 (LAMBDA # *STANDARD-OUTPUT*)))
;         (LET ((*STANDARD-OUTPUT* 2))
;           (EQL 2 (FUNCALL #:FUNC-1038)))))
; 
; caught WARNING:
;   Constant 1 conflicts with its asserted type STREAM.
;   See also:
;     The SBCL Manual, Node "Handling of Types"
; 
; compilation unit finished
;   caught 1 WARNING condition
like image 35
Joshua Taylor Avatar answered Oct 10 '22 03:10

Joshua Taylor