Re: Major pain point in SBCL and its implementation of CLOS.
Stas Boukarev <[email protected]>
| Newsgroups | gmane.lisp.steel-bank.devel |
|---|---|
| Message-ID | <CAF63=11msZhDt8vk0cGrM2b==JtyFBnwkA_MQnTxt=yZ5G-w9g@mail.gmail.com> |
You sure are saying a lot of words, but I still don't understand what
the problem is.
CL-USER> (trace sb-mop:finalize-inheritance)
(SB-MOP:FINALIZE-INHERITANCE)
CL-USER> (defclass foo () ())
#<STANDARD-CLASS COMMON-LISP-USER::FOO>
CL-USER> (make-instance 'foo)
0: (SB-MOP:FINALIZE-INHERITANCE #<STANDARD-CLASS COMMON-LISP-USER::FOO>)
0: SB-MOP:FINALIZE-INHERITANCE returned NIL
#<FOO {700735F1F3}>
CL-USER> (defclass foo () (slot))
0: (SB-MOP:FINALIZE-INHERITANCE #<STANDARD-CLASS COMMON-LISP-USER::FOO>)
0: SB-MOP:FINALIZE-INHERITANCE returned NIL
#<STANDARD-CLASS COMMON-LISP-USER::FOO>
CL-USER> (defclass foo () ())
0: (SB-MOP:FINALIZE-INHERITANCE #<STANDARD-CLASS COMMON-LISP-USER::FOO>)
0: SB-MOP:FINALIZE-INHERITANCE returned NIL
#<STANDARD-CLASS COMMON-LISP-USER::FOO>
On Thu, Dec 4, 2025 at 4:25 AM Galen_42 via Sbcl-devel
<[email protected]> wrote:
>
> Richard,
>
> I think you are asking the wrong question.
>
> Why should SBCL users be the only people in the Common Lisp world that have to add boilerplate code to all of their programs to keep their REPL working? This is a particular problem for people who develop with any kind of DAO?
>
> I get the idea of lazy finalization is a good idea for a number of reasons. What I am asking for is that SBCL to treat class redefinition at the REPL as a case where finalization needs to happen just like when an class instance is created. Is is really so hard for you to make that kind of change to SBCL that you have to make your users add boilerplate code waste time maintaining it?
>
>
> -------------------------------------------------------------------
> Never underestimate the power of human stupidity.
> - Robert Heinlein
>
> On Wednesday, December 3rd, 2025 at 4:29 PM, Richard Westhaver <[email protected]> wrote:
>
> Is it really that common? I haven't ran into it but I don't use Postmodern/the DAO classes. I would assume Pascal's boilerplate avoids the double finalization issue well enough that you wouldn't run into that cascade of errors.
>
> On Wed, Dec 3, 2025 at 5:27 PM Galen_42 via Sbcl-devel <[email protected]> wrote:
>>
>> Pascal,
>>
>> I am curious. Why don't you modify DEFCLASS so that it will do the following:
>>
>> Detect when it is in the REPL.
>> Determine if it is redefining a class.
>> If the first two conditions are true then finalize the class.
>>
>>
>> This would seem to be a fairly low cost solution to the problem without having to seriously change the design of SBCL.
>>
>> It would seem to be better for the users, rather than everyone spending time adding ad hoc code to their projects in order to keep their development environment from breaking. Much of the point of using Common Lisp is lost if the REPL keeps locking up for no easily recognized reason.
>>
>> How many thousands of man-hours of programmer time have been lost due to forgetting or debugging various solutions to this problem over the last twenty years?
>>
>> Galen
>>
>> -------------------------------------------------------------------
>> Never underestimate the power of human stupidity.
>> - Robert Heinlein
>>
>> On Wednesday, December 3rd, 2025 at 12:46 AM, Pascal Costanza <[email protected]> wrote:
>>
>> Hi,
>>
>> The class finalization protocol does not primarily exist for reasons of performance. To quote the MOP specification, it exists to "support forward-referenced superclasses, and to account for the fact that not all classes are actually instantiated".
>>
>> The workaround you describe below is not sufficient. You should state something like this:
>>
>> (let ((class (find-class 'foo nil)))
>> (when class
>> (unless (class-finalized-p class) ;;; <<<<<
>> (finalize-inheritance class))))
>>
>> Pascal
>>
>> On 3 Dec 2025, at 06:02, Galen_42 via Sbcl-devel <[email protected]> wrote:
>>
>>
>> Hello,
>>
>> I’m writing to propose a change that would eliminate what is, in 2025, still the single largest recurring waste of programmer time in the SBCL ecosystem.
>>
>> When a class is redefined interactively with DEFCLASS, SBCL deliberately does **not** call FINALIZE-INHERITANCE automatically.
>> Every other major Common Lisp implementation does it automatically.
>>
>> The consequence is well-known: every SBCL user who develops with CLOS (and especially with Postmodern, Mito, S-SQL, etc.) must manually add the same boilerplate everywhere:
>>
>> (eval-when (:compile-toplevel :load-toplevel :execute)
>> (when (find-class 'foo nil)
>> (finalize-inheritance (find-class 'foo))))
>>
>> Postmodern tries to help by calling FINALIZE-INHERITANCE itself inside its DAO-CLASS metaclass.
>> This sounds good — until you combine the two workarounds.
>> The result is **double finalization**, often at the wrong time, which triggers a cascade of obscure errors (the infamous “The value NIL is not of type STRING” during slot validation being the most common).
>> What should be a 10-second class tweak turns into hours of head-scratching, macro-expansion debugging, and package-lock fights — all because two different pieces of code are trying to paper over the same missing SBCL behaviour.
>>
>> I personally spent an entire day on this exact problem last week.
>> I have seen dozens of others do the same over the years.
>> It is the #1 cause of “it works on CCL, why does it explode on SBCL?” questions.
>>
>> The performance argument no longer holds water in 2025:
>>
>> - Finalizing a class on a modern machine is measured in microseconds.
>> - It has zero effect on runtime performance once the class is finalized.
>> - It can be gated behind interactive evaluation detection (*load-pathname* nil, REPL session, etc.) with nearly zero cost in compiled code.
>>
>> Proposed solutions (in order of preference)
>>
>> 1. **Best:** automatically finalize on redefinition when evaluated interactively
>> (detected via *load-pathname* or REPL context)
>>
>> 2. **Excellent:** a global opt-in special variable
>> (defvar *auto-finalize-on-redefinition* t)
>>
>> 3. **Still helpful:** a per-DEFCLASS declaration
>>
>> This single change would instantly remove thousands of lines of duplicated, fragile boilerplate from every SBCL codebase that uses DAOs or interactive CLOS development, and — more importantly — stop Postmodern’s well-intentioned workaround from actively interfering with the manual one.
>>
>> It would save the community hundreds of hours per year with no measurable downside.
>>
>> It seems to me that a bit of code that detects whether or not the class redefinition is taking place in the compiler or REPL would solve this for everyone with little overhead. Something like this perhaps:
>>
>> (defmacro defclass (name superclasses slots &rest options)
>> `(progn
>> (cl:defclass ,name ,superclasses ,slots ,@options)
>> ;; This branch is compiled away completely in COMPILE-FILE
>> ,(when (and (not *compile-file-truename*)
>> (not *load-truename*))
>> `(eval-when (:execute :load-toplevel)
>> (when (find-class ',name nil)
>> (myapp:create-table ',name))))))
>>
>> Thank you very much for considering this long-overdue quality-of-life improvement.
>>
>> Best regards,
>> Galen
>>
>> -------------------------------------------------------------------
>> Never underestimate the power of human stupidity.
>> - Robert Heinlein
>> _______________________________________________
>> Sbcl-devel mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/sbcl-devel
>>
>>
>> _______________________________________________
>> Sbcl-devel mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/sbcl-devel
>
>
> _______________________________________________
> Sbcl-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/sbcl-devel
_______________________________________________
Sbcl-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-devel