Stef,
Your comments brought to mind a few thoughts.
First, initialize methods should, in general, be idempotent; that is, running
them repeatedly should not make further changes after the first run (along the
lines of your mention of lazy initialization). For example, if a variable is
nil then it should be set to an empty set; if it is already a set, don’t
replace it.
Second, many packaging systems have scripts for pre-load, post-load,
pre-remove, and post-remove. Instead of having initialize methods on a class,
we could have scripts associated with a package. (Presumably a package will be
a first-class object and have methods to handle these action.)
Third, following a model from upgrading databases, one would have a “version
number” stored somewhere (perhaps a class variable) and scripts that upgrade
and downgrade the version. Rerunning the “upgrade” would be idempotent since we
would already be on the latest version.
As to the specific situation, yes, there is a problem with
Behavior>>#initialize that arises from Behavior’s unique position where
instances are classes and we have an unfortunate gratuitous polymorphism
between instance initialization in which we are creating a new instance of
Behavior (and giving it an empty method dictionary) and class initialization
which is typically associated with _loading_ or _installing_ a class. Perhaps
with a time machine the right solution would be separate names for these
concepts (#initialize for instances, and #postLoad for classes). But really,
the only place this is confusing is with Behavior, so maybe we should treat it
as a special case rather than reaching for a general solution.
I think that rather than trying to prevent calling super initialize on classes
(when failing to call super initialize on instances is a common bug) or trying
to prevent an initialize from reaching Behavior>>initialize (by blocking it on
the class side of Object), we should simply have Behavior>>initialize recognize
its special position (and unique risk for confusion) by being idempotent. That
is, if we already have a method dictionary, then there is no reason to do any
further initialization; just protect the existing code with a check of “are we
already initialized?” (Do the simplest thing that could possible work!)
So, while we could look at bigger solutions like package managers or image
versions, we should just start by making Behavior>>#initialize smart enough to
recognize its unique danger and handle the problem there. I don’t like the
situation in which some initialize methods _must_ call super and some
initialize methods _must not_ call super.
In fact, I could imagine that there are some times when class initialization
_should_ call super. Perhaps I have an abstract superclass that builds and
caches a list of its subclasses (in GemStone this would be non-trivial). When a
new subclass is added and the subclass is initialized, I want the super
initialize method to be called so the superclass can reinitialize its cache.
Perhaps this is a contrived case, but the point is that it isn’t really
appropriate to put in a rule that you should not send super initialize.
Just some ideas and thoughts to let you know I’m reading your posts…
James Foster
> On Jan 17, 2024, at 12:37 PM, stephane ducasse
> wrote:
>
> Hi community
>
> I would like to get use your ideas.
>
> Here is the case
>
> Context: Class side initialize are not good.
> We fixed a bug with guille today (should do a PR) this bug was breaking
> some projects by changing the superclass of classes to Object. Radical!
>
> It was produced by the removal of DependentFields in Object and the
> removal of the class side Object initialize method
> Needed to initialize DependentFields (RIP).
>
> Doing so when a class was doing a super initialize it executes the
> Behavior>>#initialize (which reinitialize the class and its superclass) and
> was not reachable before because blocked by Object class>>#initialize.
>
> Two Situations.
>
> When we do a class super initialize there are two cases
>
> Case 1. the super reaches Behavior>>#initialize and your class gets
> killed.
> Solution 1. Easy we will define a class side initialize method to
> protect the execution of Behavior>>#initialize.
> Ideally raising a warning so that people can find their problems.
>
> Case 2.
> You have a little hierarchy Root and Subclass1 Subclass2 classes
> Root class defines an initialize method.
>
> You should not redefine initialize in Subclass1 and do a super
> initialize
> else we get a double initialize. Not easy to grasp so I guess
> that many people are making this mistake.
>
> Solution 2.
> Cyril added a rule in a ReleaseTest (yes this is cool and we
> should use more rules and not code by hand in the releaseTest)
>