This is indeed an issue I'd like to see solved. Class initialization is just an instance of a more general isssue.
In SUnit, defining abstract classes is dirty. The code often looks like this MyTestClass class>>#isAbstract ^self == MyTestClass In PharoJS we face the issue of class specific methods. Upon transpiling to JS, we want to perform some actions for some classes and not their subclasses. A possible solution is to introduce class specific properties through an extra-layer of metaclasses. Noury On Jan 18 2024, at 3:38 pm, David Mason <[email protected]> wrote: > I like James' suggestion. > > It shouldn't matter if someone defines a class initialize or not, or whether > they call super initialize. So either Object class should have an empty one > (like Object>>initialize) or Behavior>>initialize should do the right thing. > > The Smalltalk way would be for it to go in Object class, because even > infrastructure code like Behavior should avoid condition checking when the > dispatch rules can already solve the problem. > > ../Dave > On Wed, 17 Jan 2024 at 22:52, James Foster via Pharo-dev > <[email protected] (mailto:[email protected])> wrote: > > 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] > > > (mailto:[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 > > > > > > > > > > > > > > > > >
