Monday, November 17, 2014

Common Lisp Code Snippet: Handling for Condition Created with Redefinition of Constant in SBCL

Compliant with Common Lisp the Language, 2nd Edition (CLtL2) (ANSI CL) [CLHS], SBCL emits an error -- namely, of type sb-ext:defconstant-uneql -- whenever a constant variable is redefined, to a value not eql to the variable's previous value. Though it is certainly a standards-compliant behavior, and may be towards a useful result in any indeterminate number of instances, however the behavior can cause something of a distraction when a Common Lisp software system is being compiled and subsequently loaded into the running Lisp image.

When ASDF is being applied for the compilation and loading of a Common Lisp software system,  the following method definition will effectively prevent the redefinition errors from being rendered as errors.  The warning notice, as emitted, will contain the original redefinition error, as an object within the simple-condition-format-arguments of the warning object. The message printed from the warning notice will likewise print the text of message from the original redefinition error.

#+asdf
(defmethod asdf:operate :around ((operation asdf:load-op)
                                 (component asdf:component)
                                 &key &allow-other-keys)
  (declare (ignore operation component))
  (handler-bind
      ((sb-ext:defconstant-uneql
        #'(lambda (c)
            (let ((previous 
                   (sb-ext:defconstant-uneql-old-value c))
                  (new 
                   (sb-ext:defconstant-uneql-new-value c)))
            (cond 
              ((equalp previous new)
               (warn "Redefining constant: ~A" c)
               (invoke-restart 'continue))
              (t (error c)))))))
      (call-next-method)))
This source code snippet is provided with the following footnotes:

  • This method is defined  as an :around method, to avoid collision with primary methods defined onto asdf:operation
  • The method is specialized onto the classes, asdf:load-op and   asdf:component. The class, asdf:load-op is applied as a specializer, instead of of asdf:operation, in assuming that most of the uninteresting defconstant redefinitions would occur during load-time -- typically, immediately after the same component would have been compiled
  • If the previous and new values for the constant are equalp, then the error is emitted within a warning object, as to accurately indicate the condition provided to the handler function. If the values are not equalp, then the condition is emitted as an error
  • This source code can be added to one's ~/.sbclrc file, for convenience. 
This source code is provided as an extension onto ANSI Common Lisp, in the public domain.

Works Consulted:
[1] Stack Overflow: SBCL: CONTINUE restart absent during COMPILE? (really was:absent in HANDLER-CASE)
[2] CLHS: HANDLER-BIND
[3] CLHS: HANDLER-CASE
[4] SBCL User Manual: Defining Constants