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 <[email protected]> > 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) > that checks for no class side super initialize in Pharo. > > Now long time ago I thought that this class initialize is not that good and > that I would like to have > a way that class state dependencies do not have to be resolved by the > developers by initializing first A then B > but that the system could do it automatically. In this case we would not need > initialize by class construction. > > Do you have an idea how such design could be? > > I’m too dead right now to think but to me it feels a bit similar to lazy > initialization. > If all the shared variables would be only accessed by lazy accessors then we > do not need to initialize them statically. > Now this is a bit extreme but this is to illustrate my wish. > > So let us know :) > Stef > > > > >
