[Pharo-dev] Re: About removing class side initialization

2024-02-01 Thread Guillermo Polito


> El 31 ene 2024, a las 15:36, Marcus Denker  escribió:
> 
> For both #isAbstract and #initialize, one can understand both as cases where 
> we have to use behavior for something that should actually be a kind o 
> declarative property of the objects.
> 
> - isAbstract 
> 
> This is a property of the class.
> 
> - initialize 
> 
> This is a property of the Variable. It’s really unfortunate to have to fall 
> back on code execution for that. What the initalize does is to say

There is out there indeed code that uses class side initialize methods to 
initialize class variables to constants.
But that is not the only case.

Some class side initalizes have complex initializations.
Some are used to register the loaded class to some registration mechanism.

> 
>> On 31 Jan 2024, at 15:03, Noury Bouraqadi  wrote:
>> 
>> 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.

Yes, I agree. I would like to have two levels of “Metaclasses”.
Actually, one metaclass that is the traditional metaclass defining the class 
behavior.
One “companion” object where we can define domain concerns.

The companion object should respect the double hierarchy, but it will not class 
anymore with the meta-behaviour in the class!
We will be able to freely program there without mistakenly overriding critical 
behavior from classes :).
And we will be able to define metaclasses with really specific behavior for a 
single class in the hierarchy chain.

G



[Pharo-dev] Re: About removing class side initialization

2024-01-31 Thread Marcus Denker
For both #isAbstract and #initialize, one can understand both as cases where we 
have to use behavior for something that should actually be a kind o declarative 
property of the objects.

- isAbstract 

This is a property of the class.

- initialize 

This is a property of the Variable. It’s really unfortunate to have to fall 
back on code execution for that. What the initalize does is to say

 “the default value of this Variable is this literal value” 
or
“send this message to the new object to set the default value”

Anything else that you could do in #initialize is IMHO bad style.

We always are proud that “Everything is an Object”. But as soon as we talk 
about the language itself, Smallkers suddenly forget everything we know about 
objects: Everything has to be a Class or a Method ! there is no other way!

We really need to try to free ourselves from this mindset and start to model 
our programs better.

With fluid class defintions and First Class Variables, we can do this:

Object << #MyClass
slots: {};
sharedVariables: { #ClassVar => InitializedClassVariable default: 1 };
package: ‘MyPackage’;


(this works and even is correctly stored in GIT or even when filed out)

Instead of #isAbstract, we can just tag the class as Abstract, we could add a 
method on the classBuilder:

Object << #MyClass
slots: {};
sharedVariables: { };
package: ‘MyPackage’;
beAbtract

or have some way to specify properties


Object << #MyClass
slots: {};
sharedVariables: { };
package: ‘MyPackage’;
properties: {#abstract }



There is even already a property API on class, but no support on the level of 
the class builder / class parser yet.

And of course, we have to then make sure this is stored correctly in Git.


Marcus

> On 31 Jan 2024, at 15:03, Noury Bouraqadi  wrote:
> 
> 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  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 
> mailto:pharo-dev@lists.pharo.org>> 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 

[Pharo-dev] Re: About removing class side initialization

2024-01-31 Thread Noury Bouraqadi
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  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 
> mailto:pharo-dev@lists.pharo.org)> 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  > > (mailto:stephane.duca...@inria.fr)> 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 

[Pharo-dev] Re: About removing class side initialization

2024-01-18 Thread David Mason
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 <
pharo-dev@lists.pharo.org> 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 <
> stephane.duca...@inria.fr> 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 

[Pharo-dev] Re: About removing class side initialization

2024-01-18 Thread Pablo Tesone
Yes to all, I always hated the #initialize in class side, I think we 
should divide them. It will be easy to remove the #initialize in 
Behavior and rename it to something else that is called during the real 
initialization of the objects.


In the long time I think we should change the initialize name to 
something like "doWhenTheClassIsInstaller" or maybe less verbose :P


I am not sure about lazy initialization, but it also is a nice solution.

Cheers,

Pablo

On 17/01/2024 21:37, 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)
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








[Pharo-dev] Re: About removing class side initialization

2024-01-17 Thread James Foster via Pharo-dev
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)
>