emacs-devel
[Top][All Lists]
Advanced

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

[PATCH] lisp/emacs-lisp/eieio.el (initialize-instance): Fix initform


From: akater
Subject: [PATCH] lisp/emacs-lisp/eieio.el (initialize-instance): Fix initform
Date: Wed, 30 Jun 2021 12:45:27 +0000


Attachment: signature.asc
Description: PGP signature

Attachment: 0001-lisp-emacs-lisp-eieio.el-initialize-instance-Fix-ini.patch
Description: Fix eval initform

* Summary
According to CLHS,
#+begin_quote
A default initial value form for a slot is defined by using the
:initform slot option to defclass. If no initialization argument
associated with that slot is given as an argument to make-instance or is
defaulted by :default-initargs, this default initial value form is
evaluated in the lexical environment of the defclass form that defined
it, and the resulting value is stored in the slot.
#+end_quote
--- [[clhs::07_a.htm][Object Creation and Initialization]]

This is not what happens in eieio.  Rather, initform is evaluated even
when initarg is present.

* Emacs Lisp examples
Define a class with a slot that has an initarg and an initform involving
undefined function:
#+begin_src emacs-lisp
(defclass test-initform ()
  ((test-slot :initarg :test-slot :initform (identity (non-existent)))))
#+end_src

Now,
#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(condition-case err (make-instance 'test-initform :test-slot 0)
  (t err))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
(void-function non-existent)
#+end_example

Even though initform should not be evaluated since initarg is provided.

* Other references from CLHS
#+begin_quote
If the initialization argument has a value in the initialization
argument list, the value is stored into the slot of the newly created
object, overriding any :initform form associated with the slot.
#+end_quote
--- [[clhs::07_aa.htm][Initialization Arguments]]

#+begin_quote
An :initform form is used to initialize a slot only if no initialization
argument associated with that slot is given as an argument to
make-instance or is defaulted by :default-initargs.
#+end_quote
--- [[clhs::07_ac.htm][Defaulting of Initialization Arguments]]

#+begin_quote
If a slot has both an :initform form and an :initarg slot option, and
the initialization argument is defaulted using :default-initargs or is
supplied to make-instance, the captured :initform form is neither used
nor evaluated.
#+end_quote
--- [[clhs::07_ad.htm][Rules for Initialization Arguments]]

* Common Lisp examples
Define a class in exactly the same way as in Emacs Lisp:
#+begin_src lisp :results errors :wrap example lisp
(defclass test-initform ()
  ((test-slot :initarg :test-slot :initform (identity (non-existent)))))
#+end_src

#+RESULTS:
#+begin_example lisp
; in: DEFCLASS TEST-INITFORM
;     (NON-EXISTENT)
; 
; caught STYLE-WARNING:
;   undefined function: COMMON-LISP-USER::NON-EXISTENT
; 
; compilation unit finished
;   Undefined function:
;     NON-EXISTENT
;   caught 1 STYLE-WARNING condition
#+end_example

Appropriately,
#+begin_src lisp :results value verbatim :wrap example lisp
(ignore-errors (make-instance 'test-initform))
#+end_src

#+RESULTS:
#+begin_example lisp
NIL
#<UNDEFINED-FUNCTION NON-EXISTENT {10052CF123}>
#+end_example

But with initarg, there is no need to evaluate initform, and it is not
evaluated:
#+begin_src lisp :results value verbatim :wrap example lisp
(make-instance 'test-initform :test-slot 0)
#+end_src

#+RESULTS:
#+begin_example lisp
#<TEST-INITFORM {1005224E13}>
#+end_example

* Notes
** Initializing to quoted initform
Emacs 27 source claims:
#+begin_quote
Paul Landes said in an email:
> CL evaluates it if it can, and otherwise, leaves it as
> the quoted thing as you already have.  This is by the
> Sonya E. Keene book and other things I've look at on the
> web.
#+end_quote
--- [[file:/usr/share/emacs/27.2/lisp/emacs-lisp/eieio.el::;; Paul Landes said 
in an email:][EIEIO on initform quoting]]

And eieio does quote initform forms with cars being symbols that are not fbound:
#+begin_src lisp :results none
(defclass test-initform-quote ()
  ((test-slot :initform (non-existent))))
#+end_src

#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(slot-value (make-instance 'test-initform-quote) 'test-slot)
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
(non-existent)
#+end_example

There is however no reference to Sonya E. Keene or anything else.  Meanwhile,
#+begin_src lisp :results errors :wrap example lisp
(defclass test-initform-quote ()
  ((test-slot :initform (non-existent))))
#+end_src

#+RESULTS:
#+begin_example lisp
; in: DEFCLASS TEST-INITFORM-QUOTE
;     (NON-EXISTENT)
; 
; caught STYLE-WARNING:
;   undefined function: COMMON-LISP-USER::NON-EXISTENT
; 
; compilation unit finished
;   Undefined function:
;     NON-EXISTENT
;   caught 1 STYLE-WARNING condition
#+end_example

#+begin_src lisp :results value verbatim :wrap example emacs-lisp
(ignore-errors (make-instance 'test-initform-quote))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
NIL
#<UNDEFINED-FUNCTION NON-EXISTENT {1003251A33}>
#+end_example

Also, when discussing interplay between initform and default-initargs, Sonya E. 
Keene mentions:
#+begin_quote
The value of an :initform is evaluated each time it is used to initialize a 
slot.
#+end_quote
--- Sonya E. Keene, Object Oriented Programming in Common Lisp:
    A Programmer's Guide.  9.3. Controlling Initialization
    With ~defclass~ Options

similarly stated in CLHS, in “Macro DEFCLASS”.

Emacs 28 source removes the claim but adds
#+begin_quote
FIXME: Historically, EIEIO used a heuristic to try and guess
whether the initform is a form to be evaluated or just
a constant.  We use `eieio--eval-default-p' to see what the
heuristic says and if it disagrees with normal evaluation
then tweak the initform to make it fit and emit
a warning accordingly.
#+end_quote
--- [[file:~/src/emacs/lisp/emacs-lisp/eieio.el::;; FIXME: Historically, EIEIO 
used a heuristic to try and guess][eieio in git]]

which arguably makes matters even more confusing.  There should be no
such heuristic.

* Patch
We offer a patch for Emacs 28 (master) but we can't build Emacs 28 (the
bug had been filed) so it's untested and “works for me”.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]