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 <s...@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 <b...@openinworld.com> wrote:
>>> 
>>> 
>>> 
>>> On Mon, 4 Mar 2019 at 20:08, Norbert Hartl <norb...@hartl.name> wrote:
>>> 
>>> 
>>>> Am 04.03.2019 um 03:46 schrieb Ben Coman <b...@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
>> 
>> 
> 
> 


Reply via email to