Saturday 18 April 2009

Proposal 5: Too lazy for lazy initialisation?

This is rather a general Smalltalk subject than solely related to Seaside but it is still an important subject that strikes us regularly.

The general Smalltalk concept assumes that a class is filed into an image and that during this file-in process various initialisations are performed, which henceforth stay valid during the entire life cycle of a class inside an image.

I am absolutely sure from our own experience that this is a very bad concept, which caused us a great number of wasted working days for fixing the results of this unrealistic assumption.

I don't want to waste your and my time on discussing why this initialisation at file-in time often doesn't work. The plain fact is that this is not sufficient and there are very many good reasons for it.

Therefore, I state the requirement that in general all variables but especially all statics and class variables must perform lazy initialisation, which is the only way to safely ensure that their values are correct when they are used for the first time.

Of course, this requires using getter methods as I already demanded in my porposal 4.

Expecting a programmer to think of manually initializing classes, which is often found in how initialize methods are implemented, is completely unrealistic. This must be automated and securely insured that all unneeded variables are in the expected state when a class is used.

All of our own classes work exclusively with lazy initialisation and I can't remember a single case where this simple and absolutely secure procedure did not fit.

For all imported libraries we have gone through the hard way of writing special initialisation methods that ensure that all statics and class variables are properly initialised when an image is started (typically the application). Additionally, we had to change many situations where instance variables were not properly initialized, which was typically due to implementation mistakes (we all make mistakes and this is why lazy initialisation is the by far securest form).

I have never come about any good arguments against lazy initialisation and from my really comprehensive experience (even beyond Smalltalk) I know for sure that there is no alternative.

Therefore I am asking not only the authors of the Seaside library but all Smalltalk class library developers in general to please adhere to lazy initialisation especially for statics and class variables.

You will save all of us who are using your libraries an awful lot of time!

6 comments:

  1. Lazy init can be problematic when dealing with persistence. If you have explicit transaction states, you don't want to initialize a value outside of a transaction, which would be common when using a get accessor.

    ReplyDelete
  2. Right. There are exceptions to every rule.

    I don't see that this contradicts my request for lazy-initializing at least all statics and class vars.

    We have lost many days over the time due to non-lazy-init in libraries.

    And also for *most* normal instVars lazy init is definitely safer. And it keeps knowledge in one place, which is otherwise spread over two or several methods.

    ReplyDelete
  3. Interesting statement you have about "that a class is filed into an image".
    I havent filed stuff into an image in I dont remember how many years.
    Mabye thats the source of your issues?
    or do you mean something else?
    by "filein" Im referring to: 'classes.st' asFilename fileIn.

    As for lazy init of statics. More often than not I find this causes problems for me during my home development.
    For development in my company we have an image build and initialization process that is performed before giving a new image to developers and before releasing the stripped version to customers.
    its worked quite well for the past 10+ years.

    ReplyDelete
  4. "by "filein" Im referring to: 'classes.st' asFilename fileIn."

    Yes, primarily, but in VW also parcelIn, which is not quite the same and in my view much more problematic than fileIn.

    FileIn imports the code into the current change file while ParcelIn just links the sources remain in separate parcel files. This is very problematic, because of many reasons. (This was not for you, just for other readers who might not know exactly the differences.)

    In any case I made the experience that there are several issues:
    1) The interdependency of classes. FileIn does very often nor work as smoothly as the vendors claim. Not initializing vars due to this causes many other problems as side-effects.

    2) It is illusionary to assume that class vars and statics, once filed in, always keep their values.

    3) We have to make new images from scratch regularly (every couple of months) and we then file in everything automatically. This never worked properly till we employed lazy inits.

    4) One can NEVER be really sure that all is initialized without lazy init.

    5) Bugs due to missed inits are often difficult to track - just not apparent.

    6) I have seen it many times happen that programmers (including myself) make changes to the actual methods that supply the data but forget to init the variable. We all make mistakes!

    Only lazy init ensures the wanted state under all circumstances and I would not know of one single negative aspect. Except changes in attitude for many people. :-)

    "More often than not I find this causes problems for me during my home development."

    Well, we have exactly the opposite experiences and never had any problems once that we added lazy init and a central rest at start-up. This goes together perfectly well.

    Could you specify what problems you encountered *because* of lazy init? That would be interesting. I cannot imaging any and I don't remember any.

    "have an image build and initialization process"

    So do we. Took long to develop that.

    Live is colorful. So is software!

    ReplyDelete
  5. Well back on the lazy init thing -- I try to avoid class vars where possible.
    when not possible, I dont use lazy init, but a ClassName>>initialize method which i call from a workspace.
    I also rely on getting a "nil does not understand" error to help debug what I missed when writing my #initialize code.
    on the instance side, using #initialize method ensures instvars get set to their proper starting value.

    as to problems with lazy init -- what happens when you need to check for the absense of a value? is the field empty because:
    a) the customer entered an empty string and they want it that way.
    b) my initializer hasnt been run.
    or something else...
    with lazy init, you will never know.
    if you start with nil (no lazy init) its much easier to tell. Yes you can use lazy init to set to a specific value, but then you have to manage said values and deal with cases where the customer could enter your starting value as the data he wants to see in said field.

    As for parcels -- there is a postLoad property in the VW7 parcel structures that takes care of running any initializers you want. Ive yet to see that not run when its supposed to. You can also setup prerequisite trees to ensure parcels get loaded in the right order. Setting this up the first time is very time consuming, but well worth the savings once its working.

    But if you are using the older VW5 version of visualworks (which I remember seeing somewherwe) then I can understand your frustrations with parcels.

    ReplyDelete
  6. First of all, thank you for some very constructive comments! Second, there are "many ways to Rome" and the primary goal is a clean implementation. A few comments:

    "I try to avoid class vars where possible."
    So do I but some are just needed.

    "I dont use lazy init, but a ClassName>>initialize method which i call from a workspace."
    Well, you must think of doing that. That's what I want to avoid. For me that's unsafe, because we (and I) make too many mistakes! :-)

    "using #initialize method"
    This disadvantage with one central initialize is that quite often instVars are reset during runs and where are they then re-initialized again (only this one)?

    This spreads the logic, which is in one place only with lazy. My opinion.

    "....with lazy init, you will never know."
    I lazy init only instVars where the logic expects a complex content like Collection, Dictionary etc. but never simple ones. So this does not apply to what I meant. Of course, not every simple instVar must be preset.

    I think most important is that a style is consistent. And this is not the case with many libs where, for example, a static is just used diretly assuming that it was set during fileIn. This is very unsafe.

    We are not on VW 7 for many reasons.

    ReplyDelete