managing modification of class initialization methods
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
Hi Ben, It's probably time to revisit http://smallissimo.blogspot.com/2011/08/lazy-initialization-of-shared-variab... Le lun. 4 mars 2019 à 03:47, Ben Coman <btc@openinworld.com> 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
Le lun. 4 mars 2019 à 03:47, Ben Coman <btc@openinworld.com> 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 < nicolas.cellier.aka.nice@gmail.com> wrote:
Hi Ben, It's probably time to revisit
http://smallissimo.blogspot.com/2011/08/lazy-initialization-of-shared-variab...
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
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 <btc@openinworld.com> 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
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
(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 <btc@openinworld.com> wrote:
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
On Mon, Mar 4, 2019 at 8:26 AM Sven Van Caekenberghe <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
Le lun. 4 mars 2019 à 21:35, Eliot Miranda <eliot.miranda@gmail.com> a écrit :
On Mon, Mar 4, 2019 at 8:26 AM Sven Van Caekenberghe <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
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 <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
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 <tim@testit.works> 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 <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norbert@hartl.name> wrote:
Am 04.03.2019 um 03:46 schrieb Ben Coman <btc@openinworld.com>:
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
On Tue, 5 Mar 2019 at 22:08, Sven Van Caekenberghe <sven@stfx.eu> 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
participants (6)
-
Ben Coman -
Eliot Miranda -
Nicolas Cellier -
Norbert Hartl -
Sven Van Caekenberghe -
Tim Mackinnon