managing modification of class initialization methods

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

managing modification of class initialization methods

Ben Coman
In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...

> class is initialized on load - and not when you modify it - so this can be very confusing for users  

My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
and display alerts.

Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization 
with a pragma to reset a variable when the method is saved...

    MyClass class >> referenceData
        <onSaveResetVariable: ReferenceData>
        ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Nicolas Cellier

Le lun. 4 mars 2019 à 03:47, Ben Coman <[hidden email]> a écrit :
In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...

> class is initialized on load - and not when you modify it - so this can be very confusing for users  

My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
and display alerts.

Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization 
with a pragma to reset a variable when the method is saved...

    MyClass class >> referenceData
        <onSaveResetVariable: ReferenceData>
        ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Tim Mackinnon
In reply to this post by Ben Coman
He he Ben - I was just coming over here to ask something similar …

It also struck me that a method that initialises a class variable should rerun when saved, as the mystery of class side #initialize and when its actually sent can be a bit overwhelming.

These days, we have all of the hooks to do this well don’t we? I guess the issue comes down to how far you have to go if there are method dependencies etc. So yeah - maybe a pragma to make the intent clear might be a nice way to go.

As it stands (and where I’m sure this thread comes from) - I think the clarity of:

 MyVar ifNil: [MyVar := self bigInitialisation]

makes it a bit more obvious that its a lazy initialisation and you have to re-run it (and I cheat with an example like: “MyClassVar := nil. MyClass myClassVarAccessor >>>”)

But I wonder if the lazy initialisation approach is the favoured one anyway - and am curious what others think (but still agree with Ben that class initialised vars should work better in this day and age).

Tim

> On 4 Mar 2019, at 02:46, Ben Coman <[hidden email]> wrote:
>
> In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
>
>> class is initialized on load - and not when you modify it - so this can be very confusing for users  
>
> My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> and display alerts.
>
> Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> with a pragma to reset a variable when the method is saved...
>
>    MyClass class >> referenceData
>        <onSaveResetVariable: ReferenceData>
>        ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
>
> cheers -ben


Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Ben Coman
In reply to this post by Nicolas Cellier
Le lun. 4 mars 2019 à 03:47, Ben Coman <[hidden email]> a écrit :
In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...

> class is initialized on load - and not when you modify it - so this can be very confusing for users  

My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
and display alerts.

Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization 
with a pragma to reset a variable when the method is saved...

    MyClass class >> referenceData
        <onSaveResetVariable: ReferenceData>
        ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]


On Mon, 4 Mar 2019 at 14:52, Nicolas Cellier <[hidden email]> wrote:

Interesting stuff, but after ten minutes studying it I didn't fully grok it.
So my first impression is its too much magic and I couldn't isolate what is required of a simple user.  
For comparison, could you show what a user would need to do to duplicate my example using that system, assuming the background magic was in place.

cheers -ben 
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

NorbertHartl
In reply to this post by Ben Coman


> Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
>
> In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
>
> > class is initialized on load - and not when you modify it - so this can be very confusing for users  
>
> My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> and display alerts.
>
> Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> with a pragma to reset a variable when the method is saved...
>
>     MyClass class >> referenceData
>         <onSaveResetVariable: ReferenceData>
>         ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
>

Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?


Norbert
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Ben Coman


On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:


> Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
>
> In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
>
> > class is initialized on load - and not when you modify it - so this can be very confusing for users 
>
> My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> and display alerts.
>
> Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> with a pragma to reset a variable when the method is saved...
>
>     MyClass class >> referenceData
>         <onSaveResetVariable: ReferenceData>
>         ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
>

Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?

To me a good minute to work out why I didn't understand you.
Sorry, I meant <onMethodSaveResetVariable: ReferenceData>

So when 'reference data' is updated and the modified method is saved,
the variable gets lazy initialized *again* with the new definition. 

hope that is more clear,
cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Sven Van Caekenberghe-2
(1) the basic concepts are clear (and have been for a long time):

- when a class initialize method is loaded, it is executed afterwards, if and only if the source code changed

- there are startUp[:] and shutDown[:] and SessionManager to handle images coming up, saving and going down

With these tools you can build any behaviour you want.

I am not sure we need something else, except maybe more education


(2) the problem is much larger than just the class initialize method, since that can call other methods (like #initializeConstants, etc, ..) - even if the class initialize method did not change, a method further down might have and could require re-initialization.

For this reason I sometimes put a timestamp in a comment of the class initialize method to force re-initialization since I known that I added a new constant somewhere (much) further down.


Complex new features might do more harm than good

> On 4 Mar 2019, at 17:13, Ben Coman <[hidden email]> wrote:
>
>
>
> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:
>
>
> > Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
> >
> > In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
> >
> > > class is initialized on load - and not when you modify it - so this can be very confusing for users  
> >
> > My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> > and display alerts.
> >
> > Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> > with a pragma to reset a variable when the method is saved...
> >
> >     MyClass class >> referenceData
> >         <onSaveResetVariable: ReferenceData>
> >         ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
> >
>
> Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?
>
> To me a good minute to work out why I didn't understand you.
> Sorry, I meant <onMethodSaveResetVariable: ReferenceData>
>
> So when 'reference data' is updated and the modified method is saved,
> the variable gets lazy initialized *again* with the new definition.
>
> hope that is more clear,
> cheers -ben


Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Eliot Miranda-2


On Mon, Mar 4, 2019 at 8:26 AM Sven Van Caekenberghe <[hidden email]> wrote:
(1) the basic concepts are clear (and have been for a long time):

- when a class initialize method is loaded, it is executed afterwards, if and only if the source code changed

- there are startUp[:] and shutDown[:] and SessionManager to handle images coming up, saving and going down

With these tools you can build any behaviour you want.

I am not sure we need something else, except maybe more education


(2) the problem is much larger than just the class initialize method, since that can call other methods (like #initializeConstants, etc, ..) - even if the class initialize method did not change, a method further down might have and could require re-initialization.

For this reason I sometimes put a timestamp in a comment of the class initialize method to force re-initialization since I known that I added a new constant somewhere (much) further down.


Complex new features might do more harm than good

+1000.  KISS.
 

> On 4 Mar 2019, at 17:13, Ben Coman <[hidden email]> wrote:
>
>
>
> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:
>
>
> > Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
> >
> > In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
> >
> > > class is initialized on load - and not when you modify it - so this can be very confusing for users 
> >
> > My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> > and display alerts.
> >
> > Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> > with a pragma to reset a variable when the method is saved...
> >
> >     MyClass class >> referenceData
> >         <onSaveResetVariable: ReferenceData>
> >         ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
> >
>
> Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?
>
> To me a good minute to work out why I didn't understand you.
> Sorry, I meant <onMethodSaveResetVariable: ReferenceData>
>
> So when 'reference data' is updated and the modified method is saved,
> the variable gets lazy initialized *again* with the new definition.
>
> hope that is more clear,
> cheers -ben




--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Nicolas Cellier


Le lun. 4 mars 2019 à 21:35, Eliot Miranda <[hidden email]> a écrit :


On Mon, Mar 4, 2019 at 8:26 AM Sven Van Caekenberghe <[hidden email]> wrote:
(1) the basic concepts are clear (and have been for a long time):

- when a class initialize method is loaded, it is executed afterwards, if and only if the source code changed

- there are startUp[:] and shutDown[:] and SessionManager to handle images coming up, saving and going down

With these tools you can build any behaviour you want.

I am not sure we need something else, except maybe more education


(2) the problem is much larger than just the class initialize method, since that can call other methods (like #initializeConstants, etc, ..) - even if the class initialize method did not change, a method further down might have and could require re-initialization.

For this reason I sometimes put a timestamp in a comment of the class initialize method to force re-initialization since I known that I added a new constant somewhere (much) further down.


Complex new features might do more harm than good

+1000.  KISS.

Except that things are not simple if you have nested dependencies:
You have no way to control initialization order!

If (A initialize) will instantiate a B new that require (B initialize) then you are screwed.
You then have to create several packages and load them in the right order to work around the initialization.

This is one reason for resorting to lazy initalization: disantangle such initialization dependencies.
But hardcoding every lazy initialization does not sound right.

    A class>>accessMyClassVar
        ^MyClassVar ifNil: [self initializeMyClassVar]

Having a way to link the method #initializeMyClassVar to MyClassVar with a declarative form by way of #<initialize> annotation is more universal/nice.

    A class>>initializeMyClassVars
        #<initialize>
        MyClassVar := 1.
        MyOtherClassVar := B new: 10.

Having an UnitializedStatic instance automatically becomeForward: whatever initialized object is simple and legitimate magic.
I agree that maintaining the registry that links initializer to this variable is the complex part
The easy part is when adding a new initializer method (this is the case of installing a package in image).
The harder part is when changing or removing a previous initializer (exactly like what happened to you recently with FFI).

 

> On 4 Mar 2019, at 17:13, Ben Coman <[hidden email]> wrote:
>
>
>
> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:
>
>
> > Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
> >
> > In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
> >
> > > class is initialized on load - and not when you modify it - so this can be very confusing for users 
> >
> > My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
> > and display alerts.
> >
> > Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
> > with a pragma to reset a variable when the method is saved...
> >
> >     MyClass class >> referenceData
> >         <onSaveResetVariable: ReferenceData>
> >         ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
> >
>
> Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?
>
> To me a good minute to work out why I didn't understand you.
> Sorry, I meant <onMethodSaveResetVariable: ReferenceData>
>
> So when 'reference data' is updated and the modified method is saved,
> the variable gets lazy initialized *again* with the new definition.
>
> hope that is more clear,
> cheers -ben




--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Tim Mackinnon
In reply to this post by Sven Van Caekenberghe-2
If we go back to where this comes from - I don’t think its so obvious what the behaviour is. Newcomers to Smalltalk don’t get it, and I suspect even seasoned Smalltalkers have to think twice or more about this.

If we roll back to where I think this question came from:

If I have a simple constant like #maxLimit - I can stick it as an instance method as:

maxLimit
        ^42


But if my constant is more complicated like:

massTable
        | multiplier |
        multipier := 4.25678.

        ^{ ‘a’ -> 27 * multiplier.
        ‘b’ -> 42 * multiplier.
        …
        ‘z’ -> 106 * multiplier } asDictionary

It's not ideal to have this as an instance method - so you are advised to move it up to the class level, and try to emulate a “const”.

But this still isn’t good enough as its evaluated every time - so you add a class variable MassTable and initialize it once, in class>>initialize

Initialise

        MassTable := self massTable.


Ok - so far so good - except you made a mistake and want to correct the value for ‘z’, which you change from 106 to 110 and press save.

But surprise, surprise, it doesn’t change - because when is class #initialise actually called? Trick question it turns out - as if I make the change in GitHub, commit the code and then reload the package with iceberg - it still won’t change as reloading code doesn’t cause class initialise (which is surprising to most).

I think that this this quite common occurrence shouldn’t be so mysterious - why can’t I just change the code, save it and have the value change in some obvious manor without some voodoo. This is one of those strange cases of smalltalk where it doesn’t obviously do what I expect and I need to know too much of the system to do the right thing.

I think we could come up with a way to make this easier - but It would be good to get the wisdom of the crowd in on this.

This is one of the early exercise in Exercism, and we’d like to know the best, element, in keeping with the philosophy solution.

At the moment, a class variable with a lazy getter is the suggested solution e.g.

massTable
        “MyClass MassTable := nil. self massTable >>>”

        ^MassTable ifNil: [ MassTable :=  self createMassTable ]

Where you have to remember to click on the example icon, or evaluate a doIt is the best solution.

To me - this is WTF? Surely we can do better in an environment where we control the compiler, ide and interaction model?

Tim



> On 4 Mar 2019, at 16:26, Sven Van Caekenberghe <[hidden email]> wrote:
>
> (1) the basic concepts are clear (and have been for a long time):
>
> - when a class initialize method is loaded, it is executed afterwards, if and only if the source code changed
>
> - there are startUp[:] and shutDown[:] and SessionManager to handle images coming up, saving and going down
>
> With these tools you can build any behaviour you want.
>
> I am not sure we need something else, except maybe more education
>
>
> (2) the problem is much larger than just the class initialize method, since that can call other methods (like #initializeConstants, etc, ..) - even if the class initialize method did not change, a method further down might have and could require re-initialization.
>
> For this reason I sometimes put a timestamp in a comment of the class initialize method to force re-initialization since I known that I added a new constant somewhere (much) further down.
>
>
> Complex new features might do more harm than good
>
>> On 4 Mar 2019, at 17:13, Ben Coman <[hidden email]> wrote:
>>
>>
>>
>> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:
>>
>>
>>> Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
>>>
>>> In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
>>>
>>>> class is initialized on load - and not when you modify it - so this can be very confusing for users  
>>>
>>> My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
>>> and display alerts.
>>>
>>> Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
>>> with a pragma to reset a variable when the method is saved...
>>>
>>>    MyClass class >> referenceData
>>>        <onSaveResetVariable: ReferenceData>
>>>        ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
>>>
>>
>> Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?
>>
>> To me a good minute to work out why I didn't understand you.
>> Sorry, I meant <onMethodSaveResetVariable: ReferenceData>
>>
>> So when 'reference data' is updated and the modified method is saved,
>> the variable gets lazy initialized *again* with the new definition.
>>
>> hope that is more clear,
>> cheers -ben
>
>


Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Sven Van Caekenberghe-2
Tim,

Your example is exactly the most common 'gotcha' case where something unexpected happens.

In that case, when one of the underlying #massTable constants/specs changes, I would add a comment timestamp in the class #initialize method, something along the lines "last change: 20190305".

You are right that this is a complex and not so obvious issue, both for newcomers as for others.

The key cause of this is that we have a live image that lives longer that a classic program, people are not used to this. Class variables hold state, sometimes even complex databases.

The decision to not always rerun all class #initialize methods is because sometimes these might hold expensive calculations, another reason might be that not all class #initialize methods can run in all circumstances (they might want to load external data, etc, ...).

Now, your solution, the pragma, is just as complex, because it would still require people to (1) understand the issue and (2) remember to add the pragma correctly. If the dependency goes over different classes, with potentially unknown users, it might even be impossible to solve.

Sven

> On 5 Mar 2019, at 01:50, Tim Mackinnon <[hidden email]> wrote:
>
> If we go back to where this comes from - I don’t think its so obvious what the behaviour is. Newcomers to Smalltalk don’t get it, and I suspect even seasoned Smalltalkers have to think twice or more about this.
>
> If we roll back to where I think this question came from:
>
> If I have a simple constant like #maxLimit - I can stick it as an instance method as:
>
> maxLimit
> ^42
>
>
> But if my constant is more complicated like:
>
> massTable
> | multiplier |
> multipier := 4.25678.
>
> ^{ ‘a’ -> 27 * multiplier.
> ‘b’ -> 42 * multiplier.
> …
> ‘z’ -> 106 * multiplier } asDictionary
>
> It's not ideal to have this as an instance method - so you are advised to move it up to the class level, and try to emulate a “const”.
>
> But this still isn’t good enough as its evaluated every time - so you add a class variable MassTable and initialize it once, in class>>initialize
>
> Initialise
>
> MassTable := self massTable.
>
>
> Ok - so far so good - except you made a mistake and want to correct the value for ‘z’, which you change from 106 to 110 and press save.
>
> But surprise, surprise, it doesn’t change - because when is class #initialise actually called? Trick question it turns out - as if I make the change in GitHub, commit the code and then reload the package with iceberg - it still won’t change as reloading code doesn’t cause class initialise (which is surprising to most).
>
> I think that this this quite common occurrence shouldn’t be so mysterious - why can’t I just change the code, save it and have the value change in some obvious manor without some voodoo. This is one of those strange cases of smalltalk where it doesn’t obviously do what I expect and I need to know too much of the system to do the right thing.
>
> I think we could come up with a way to make this easier - but It would be good to get the wisdom of the crowd in on this.
>
> This is one of the early exercise in Exercism, and we’d like to know the best, element, in keeping with the philosophy solution.
>
> At the moment, a class variable with a lazy getter is the suggested solution e.g.
>
> massTable
> “MyClass MassTable := nil. self massTable >>>”
>
> ^MassTable ifNil: [ MassTable :=  self createMassTable ]
>
> Where you have to remember to click on the example icon, or evaluate a doIt is the best solution.
>
> To me - this is WTF? Surely we can do better in an environment where we control the compiler, ide and interaction model?
>
> Tim
>
>
>
>> On 4 Mar 2019, at 16:26, Sven Van Caekenberghe <[hidden email]> wrote:
>>
>> (1) the basic concepts are clear (and have been for a long time):
>>
>> - when a class initialize method is loaded, it is executed afterwards, if and only if the source code changed
>>
>> - there are startUp[:] and shutDown[:] and SessionManager to handle images coming up, saving and going down
>>
>> With these tools you can build any behaviour you want.
>>
>> I am not sure we need something else, except maybe more education
>>
>>
>> (2) the problem is much larger than just the class initialize method, since that can call other methods (like #initializeConstants, etc, ..) - even if the class initialize method did not change, a method further down might have and could require re-initialization.
>>
>> For this reason I sometimes put a timestamp in a comment of the class initialize method to force re-initialization since I known that I added a new constant somewhere (much) further down.
>>
>>
>> Complex new features might do more harm than good
>>
>>> On 4 Mar 2019, at 17:13, Ben Coman <[hidden email]> wrote:
>>>
>>>
>>>
>>> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <[hidden email]> wrote:
>>>
>>>
>>>> Am 04.03.2019 um 03:46 schrieb Ben Coman <[hidden email]>:
>>>>
>>>> In relation to developing sample solutions for an Exercism exercise, the following observation was made about class initialization...
>>>>
>>>>> class is initialized on load - and not when you modify it - so this can be very confusing for users  
>>>>
>>>> My first thought was to wonder if Quality Assistant could track whether a class initialize method had been run after it was modified,
>>>> and display alerts.
>>>>
>>>> Alternatively, I wonder if a reasonable pattern would be to couple class-side lazy initialization
>>>> with a pragma to reset a variable when the method is saved...
>>>>
>>>>   MyClass class >> referenceData
>>>>       <onSaveResetVariable: ReferenceData>
>>>>       ^ ReferenceData := ReferenceData ifNil: [ 'reference data' ]
>>>>
>>>
>>> Isn’t the usual way to do that to register the class in the shutdown list and implement #shutdown: ?
>>>
>>> To me a good minute to work out why I didn't understand you.
>>> Sorry, I meant <onMethodSaveResetVariable: ReferenceData>
>>>
>>> So when 'reference data' is updated and the modified method is saved,
>>> the variable gets lazy initialized *again* with the new definition.
>>>
>>> hope that is more clear,
>>> cheers -ben
>>
>>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: managing modification of class initialization methods

Ben Coman
On Tue, 5 Mar 2019 at 22:08, Sven Van Caekenberghe <[hidden email]> wrote:

Now, your solution, the pragma, is just as complex, because it would still require people to (1) understand the issue and (2) remember to add the pragma correctly. 

I agree those points apply in relation to creating code, 
but for an existing method with such a pragma I believe it would be reasonably easy for newcomers to read and understand the impact of modifying the data,
but I've really nothing to back up that belief.

cheers -ben