Common Lisp: check if lexical variable exists?
Asked Answered
A

2

6

How do I detect if a lexical variable is bound in a scope? I basically want boundp for lexical variables.

Concretely, say I have:

(defvar *dynamic* 1)
(defconstant +constant+ 2)

(let ((lexical 3))
  (when (boundp '*dynamic*)  ; t
    (print "*dynamic* bound."))
  (when (boundp '+constant+) ; t
    (print "+constant+ bound."))
  (when (boundp 'lexical)    ; nil
    (print "lexical bound.")))

So boundp correctly checks for dynamic variables (and constants), and as the hyperspec says, doesn't cover lexical bindings.

But I can't find any equivalent of boundp for lexical bindings. So how do I check them then? (Implementation-specific code for say SBCL is fine if there isn't anything portable.)

Afterbody answered 5/6, 2016 at 15:53 Comment(3)
What are you going to use that for?Jewfish
I want to provide some extra safety (against typos etc) to a macro operating on variables, including lexical ones. (Beyond what the compiler already does.) boundp works for lexical bindings in Emacs Lisp, so I already have some Emacs Lisp code that works that way, and would like to port it in the most simple and straightforward way.Afterbody
(Oops, I was confused about Emacs Lisp: if lexical-binding is in effect for a given file, boundp works like in Common Lisp here. It just so rarely is in effect in my code, I didn't even notice. :))Afterbody
B
9

There is nothing like that in ANSI Common Lisp. There is no access to a lexical environment.

You only can check it this way:

CL-USER 8 > (let ((lexical 3))
              (when (ignore-errors lexical) 
                (print "lexical bound."))
              (values))

"lexical bound." 

CL-USER 9 > (let ((lexical 3))
              (when (ignore-errors lexxxical) 
                (print "lexical bound."))
              (values))
<nothing>

There is no way to take a name and see if it is lexically bound at all. There is an extension to CL, where the function variable-information would give some information, but even in this case it would probably not work:

* (require "sb-cltl2")

("SB-CLTL2")
* (apropos "variable-information")

VARIABLE-INFORMATION
SB-CLTL2:VARIABLE-INFORMATION (fbound)
* (let ((lexical 3))
     (sb-cltl2:variable-information 'lexical))
; in: LET ((LEXICAL 3))
;     (LET ((LEXICAL 3))
;       (SB-CLTL2:VARIABLE-INFORMATION 'LEXICAL))
; 
; caught STYLE-WARNING:
;   The variable LEXICAL is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition

NIL
NIL
NIL
Bluhm answered 5/6, 2016 at 17:51 Comment(0)
C
2

for cltl2:variable-information to work, it should be done in macro expansion time.

(ql:quickload :introspect-environment)
(use-package :introspect-environment) ;; also exports cltl2 functions.

(defmacro in-compile-time ((environment) &body body &environment env)
  (check-type environment symbol)
  (eval `(let ((,environment ,env)) (progn ,@body)))
  nil) ; does not affect the expansion

(defun fn ()
  (let ((lexical 2))
    (in-compile-time (env)
      (print (introspect-environment:variable-information 'lexical env))
      (print (introspect-environment:variable-information 'lexxxxical env)))))
; compiling (DEFUN FN ...)
:LEXICAL 
NIL 
Carranza answered 9/1, 2018 at 11:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.