random-state.net

Nikodemus Siivola

<< next | top | previous >>

November 4th 2005 #
random, November 4th 2005

Oh Constant, My Constant!

ANSI Common Lisp defines CONSTANTP rather loosely: self-evaluting objects, keywords, constant variables, and quote forms are about all one can portably rely on.

Implementation are, however, permitted to do better. We may identify arbitrarily complex forms as constant — including expanding macros and inlining functions.

SBCL has not been very ambitious in this respect so far. However, at the prompting of James Knight on sbcl-devel I hacked up a small proof of concept:

CL-USER> (constantp '(+ 1 (/ 2 3)))
T
CL-USER> (defmacro foo (x) `(* ,x (+ 1 (/ 2 3))))
FOO
CL-USER> (constantp '(foo 0))
T
CL-USER> (constantp '(foo x))
NIL

My version is sufficient to deal with macros and constant-foldable function calls. Inline functions aren't deal with, nor are any special forms except QUOTE.

I'm not sure how soon this will make it into CVS, as there are a few potential problems this exposes within SBCL: sometimes we use CONSTANTP without passing in the current environment. Up till now this didn't matter as our CONSTANTP used to ignore the environment argument. Unfortunately the new version needs it for macroexpansion...

Consider this:

(defun foo () 13)
(defmacro bar () 42)

(defmacro frob (form &environment env)
  (if (constantp form env)
      (sb-int:constant-form-value form env)
      form))

(macrolet ((bar () '(foo)))
  (frob (bar)))

This correctly evaluates to 13. However, if FROB didn't pass the environment to CONSTANTP it would evaluate to 42, as CONSTANTP would not then have seen the MACROLET.

Cleaning up our own act here is a an order of more magnitude work then the new CONSTANTP itself.