[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: strange 1+/1- bug?



Richard Shepherd writes:
> 
> (defun 1+ (x) (+ x 1))
> -> ** - Continuable Error
> Redefining the COMMON LISP function 1+
> If you continue (by typing 'continue'): The old definition will be
> lost
> 1. Break> continue
> -> 1+
> (1+ 3)
> -> 4
> (compile '1+)
> -> 1+
> (1+ 3)
> -> *****hangs***** i.e. never returns, infinite loop, etc.

Of course. The CLISP compiler recognizes that (+ x 1) is equivalent to
(1+ x) and thus generates a tail-recursive call, just as if you had written
  (defun 1+ (x) (1+ x)).


> (defun 1+ (x) (+ x 1))
** - Continuable Error
Redefining the COMMON LISP function 1+
If you continue (by typing 'continue'): The old definition will be lost
1. Break> continue
1+

> (disassemble #'1+) ;; disassemble without installing the compiled definition
Disassembly of function 1+
1 required arguments
0 optional arguments
No rest parameter
No keyword parameters
0     L0
0     (LOAD&PUSH 1)
1     (JMPTAIL 1 3 L0)
#<COMPILED-CLOSURE 1+>

> Is this a bug that is fixed in later versions?

No, this isn't a bug. You are not allowed to modify built-in Common Lisp
functions (see CLtL2 or dpANS), and that's what the warning was about.

> What's so special about the 1 in addition?

The compiler optimizes (+ x 1), but not (+ x 2).

> PS: this all came about 'cos I was simulating operator overloading in
> CLOS and decided to redefine 1+ and 1- just to be on the safe side (I
> know it probably throws away a lot of efficiency, but doesn't CLOS
> anyway?)

Using CLOS for arithmetic operations provides a great flexibility.

But redefining 1+ is a no-no. That's what Common Lisp packages are for.
Here is an example of code, written by Michael Stoll, so you get the
idea.

=========================== part of domain.lsp ===========================
(provide :domain)

(defpackage "DOMAIN"
  (:use "LISP" "CLOS")
  (:shadow "NUMBERP" "=" "ZEROP" "INTEGERP" "EXQUO" "EXPT" 
           "NUMERATOR" "DENOMINATOR" "+" "-" "*" "/" "GCD" "LCM")
  (:export "DIVIDE" "NORMALIZE" "DOMAINIFY"))

(in-package :domain)

(eval-when (load eval compile) (pushnew ':DOMAIN *features*))

(eval-when (load eval)
  (defmacro domainify ()
    `(progn
       (defpackage ,(package-name *package*)
         (:shadowing-import-from "DOMAIN"
              "NUMBERP" "=" "ZEROP" "INTEGERP" "EXQUO" "EXPT" 
              "NUMERATOR" "DENOMINATOR" "+" "-" "*" "/" "GCD" "LCM"
              "DIVIDE" "NORMALIZE")
         (:use ,@(mapcar #'package-name (package-use-list *package*)))))))

;; At the beginning of every program which uses the domain package
;; you write (after the in-package form)
;;   #+DOMAIN (domain:domainify)

============================================================================


Bruno Haible                                     email: <haible@ilog.fr>
Software Engineer                                phone: +33-1-49083585