37 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
			
		
		
	
	
			37 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
(in-package :alexandria)
 | 
						|
 | 
						|
(defun %reevaluate-constant (name value test)
 | 
						|
  (if (not (boundp name))
 | 
						|
      value
 | 
						|
      (let ((old (symbol-value name))
 | 
						|
            (new value))
 | 
						|
        (if (not (constantp name))
 | 
						|
            (prog1 new
 | 
						|
              (cerror "Try to redefine the variable as a constant."
 | 
						|
                      "~@<~S is an already bound non-constant variable ~
 | 
						|
                       whose value is ~S.~:@>" name old))
 | 
						|
            (if (funcall test old new)
 | 
						|
                old
 | 
						|
                (restart-case
 | 
						|
                    (error "~@<~S is an already defined constant whose value ~
 | 
						|
                              ~S is not equal to the provided initial value ~S ~
 | 
						|
                              under ~S.~:@>" name old new test)
 | 
						|
                  (ignore ()
 | 
						|
                    :report "Retain the current value."
 | 
						|
                    old)
 | 
						|
                  (continue ()
 | 
						|
                    :report "Try to redefine the constant."
 | 
						|
                    new)))))))
 | 
						|
 | 
						|
(defmacro define-constant (name initial-value &key (test ''eql) documentation)
 | 
						|
  "Ensures that the global variable named by NAME is a constant with a value
 | 
						|
that is equal under TEST to the result of evaluating INITIAL-VALUE. TEST is a
 | 
						|
/function designator/ that defaults to EQL. If DOCUMENTATION is given, it
 | 
						|
becomes the documentation string of the constant.
 | 
						|
 | 
						|
Signals an error if NAME is already a bound non-constant variable.
 | 
						|
 | 
						|
Signals an error if NAME is already a constant variable whose value is not
 | 
						|
equal under TEST to result of evaluating INITIAL-VALUE."
 | 
						|
  `(defconstant ,name (%reevaluate-constant ',name ,initial-value ,test)
 | 
						|
     ,@(when documentation `(,documentation))))
 |