Login

Lazy Initialization Smalltalk Style

This will be the first in hopefully a series of smaller posts covering common Smalltalk idioms for those not already familiar with Smalltalk style and contrasting to a more well known language for those not already familiar with Smalltalk syntax. Now, for a real expert on the subject of idioms, I'd recommend reading Smalltalk Best Practice Patterns by Kent Beck, but I'll offer up my limited experience on the subject as well, because hey, I have to write about something.

A common thing one wants to do, in any object oriented language, is to initialize an instance variable lazily on first access. In C# this might look like...

public string SearchType {
    get { 
        if(searchType == null)
            searchType = "AddressSearch";
        return searchType; 
    }
    set { searchType = value; }
}

In Smalltalk, I'd write that like this...

searchType
    ^searchType ifNil:[searchType := #AddressSearch]

searchType: aSymbol
    searchType := aSymbol

Symbols are often used where strings might be used in languages that lack them. The method #ifNil: when evaluated on a non nil object, simply returns the object itself, but when evaluated on a nil object (nil is a real object in Smalltalk), #ifNil: evaluates the block it was given, and returns the result of the block, which in a block, is always the last expression evaluated, in this case, the value we just set #AddressSearch. So we're setting and returning the value in a single line. Smalltalk has many such conveniences to allow one to write very terse code that's extremely readable.

Now, being object oriented code, there's a chance this class could at some later date be subclassed. If so, the programmer doing so might not like the default you've chosen, and this code offers him no chance to override your decision. If I intended this code to be part of a framework, i.e. subclassed, I'd extract that default value into a method of its own like so...

public string SearchType {
    get { 
        if(searchType == null)
            searchType = DefaultSearchType;
        return searchType; 
    }
    set { searchType = value; }
}

protected string DefaultSearchType {
    get { return "AddressSearch"; }
}

In Smalltalk, I'd write that like this...

searchType
    ^searchType ifNil:[searchType := self defaultSearchType]

defaultSearchType
    ^#AddressSearch

searchType: aSymbol
    searchType := aSymbol

I'd then categorize these in the method categories "accessing" and "accessing defaults". This would be somewhat equivalent to a region in C#, but far better because categories, as far as the code browser is concerned, actually contain those methods, and I can drag and drop methods around to categorize them.

Learning the common category names greatly helps one when browsing existing Smalltalk code, and they act as a form of documentation that you don't really get in other languages. Regions have a similar intent, but their implementation fails to provide the same value.

On thing to always remember when using lazy accessors, is that one can't ever access the instance variable directly from inside the class. You need to always use the accessor method. I've waffled back and forth between direct variable access and lazy initialization several times, but I've finally chosen lazy initialization as my preferred method because it allows one to grow a program better by allowing existing objects to be upgraded on the fly without worrying about uninitialized instance variables. I also enjoy being lazy.

Comments (automatically disabled after 1 year)

Sebastian 3283 days ago

I like to do this to lazy initialization of instVars:

Budget>>commission ^ commission ifNil:[self initializeCommission]

Budget>>initializeCommission ^ commission := 15

I've seen that from a colleague and adopted it because it allows to refine when you scale design. If I had a subclass of budget with different default commission for this silly example.

I'm sure I have not such imagination to give a real good example right now, but I use it systematically and I have no complains of this practice.

Ramon Leon 3283 days ago

That's good too, I picked up my #default technique from Lukas in Magritte, I got to really like looking for #defaultX to override.

Sebastian 3282 days ago

for some Seaside components I've just used it again like this:

layout ^ layout ifNil:[self initializeLayout]

initializeLayout ^ layout := self makeLayout

several subclasses implement #makeLayout as they find proper for it's needs as we already wrote about.

What is new here is that when you need to remake the layout at every refresh of after an updater for instance. As the layout it's customized at the #makeLayout method, the method #initializeLayout can be used as if it where a #reset and any subclass is guaranteed to have a fresh custom layout. By the way, for syntactic edulcorated rigurosity one can do at superclass:

resetLayout ^ self initializeLayout

Ramon Leon 3280 days ago

Interesting approach, in the same scenario I'd just nil the existing layout knowing it would reload when accessed again.

Your approach does create some symmetry between initializing for the whole class and initializeX for each instance var that I like. It also has the attribute of being more of a command, where my #defaultX's are a request, I like this as well, it feels more OO and as you say, makes it more amenable to reuse.

My approach requires I know the variable is lazy loaded, not a huge deal, but perhaps less encapsulated than yours. I think I might like #initializeX better than #defaultX, I'll have to try it on for a bit and see how it feels. What other idioms have you found and adopted?

Amos 3210 days ago

I feel compelled to point out the danger inherent in Sebastian's implementation: you are relying on the fact that the initializeX method returns the value that was just initialized. However, an initializeX method is typically a "do something" method, not a "get something" method; that is, you shouldn't count on it returning anything specific (let Smalltalk return self by default). What happens if you or someone else extends the initializeX method in your example later on (in that class or a subclass) to something like

initializeCommission commission := 15. self changed: #commission

because you want to set up a dependency? Your accessor should therefore look more like

commission
    ^commission ifNil:[
        self initializeCommission.
        commission ]

to return commission explicitly, irrespective of what initializeCommission returns.

I find the defaultX methods a good pattern to follow, though they really belong on the class side, not the instance side since they define behaviour common to the class and not variable from instance to instance. Personally, I tend to avoid lazy initialization unless it's a rarely-used instance variable with a more-than-trivial memory impact, but that's more due to preference than to having a definite reason against it.

Great blogs by the way, Ramon, very informative =o)

Ramon Leon 3210 days ago

That's a good observation about initializeX normally being a command rather than query, however, I'd consider it an acceptable idiom because like #add: returning what's been added, it's damn useful. An initializeX method returning the initialized value is easy to remember, and much nicer an option than...

commission
  ^commission ifNil:
      [self initializeCommission.
      commission ]

And I disagree about them belonging on the class side, because the class is an object in its own right, and might have its own defaultX methods. Sebastian's initialize approach makes this even clearer by extending the initialize pattern to the field level, on the instance side, where it belongs. Having tried Sebastian's method for a short time, I find I quite like it, and plan on continuing to use it in the future.

Amos 3209 days ago

Not to split hairs with you ;-), but when making available your code for others rather than just programming for yourself, you should avoid "acceptable idioms" and hidden assumptions as much as possible. If you want to do it that way, I'd recommend at least putting a comment in the initializeX method that there are senders that assume a certain return value, so that others who don't know about your assumptions will know what to watch out for when extending your code. Such things should also be written in the class comment. A more elaborate (but less refactored) version of the method I mentioned would be

commission commission isNil ifTrue: [self initializeCommission]. ^commission

The reason behind it is the same as adding #yourself to the end of a cascade of messages sent to a collection when you want to assign the result to a variable, even if the particular message prior to #yourself for the particular collection you're using happens to return the collection. Other collections may do it differently, and somewhere down the track someone else may alter/extend your code to use a different collection.

Don't get me wrong, I've got nothing against Sebastian's pattern itself, and if the code works for you the way you had it, great - I was just pointing out that certain undocumented assumptions may lead to problems for others. Code is most often written once by one person, but read many times by many different people. ;-)

As for the defaultX methods being on the instance or the class side (note I didn't say the initializeX methods belong on the class side), of course the class is an object itself (a singleton instance), but it's also a blueprint for every instance based on it. So if a default is common to all instances, why re-define the value for every instance and add unnecessary overhead? Define it on the class side, add an accessor to return "self class defaultX" on the instance side, and away you go. (If a default was common to all classes, you'd put it on Metaclass. Unless I misunderstood what you meant about a class' default clashing with an instance's default?) If the default value is in any way dependent on an instance variable, then of course the defaultX method belongs on the instance side. Otherwise, if it's "static" (also the Java word for "class side"), I'd put it on the class every time. No big deal for small classes of which there aren't going to be many instances, but when dealing with larger classes of which there may be hundreds or thousands of instances, a larger memory footprint becomes more important in my experience. And good habits live long, bad habits die hard ;-)

Ramon Leon 3209 days ago

You make quite a few good points, enough to make me think about it. I agree with you about initializeX having a return value being a suprise, and you've a good argument for not doing that. Maybe defaultX is a better idea because it's clear a value is being returned and there are no suprises.

"So if a default is common to all instances, why re-define the value for every instance and add unnecessary overhead?"

A good point, however, thinking about it a bit, I think I value the organization and symmetry better if instance side defaults are defined on the instance side and class side defaults are defined on the class side.

Thinking about it even more, I could easily have the same documentation advantage by having a two method categories for defaults on the class side, instance defaults and class defaults.

I think I'll have to go back the defaultX style, but on the class side organized via method categories. Appreciate the comment Amos. These are the kinds of little idioms I love so much about good Smalltalk, naming and organization are so important and Smalltalker's seem to really care enough to make the effort.

Amos 3209 days ago

The great thing about Smalltalk (well, one of them anyway) is that there's no clearly defined "right" and "wrong" way of doing things (at least not globally, though of course there can be set standards within a company; I'm reminded of my team leader and code-reviewer at my first Smalltalk job whom we used to call "The Code-Nazi"... I learned a heck of a lot from him!), as long as one keeps in mind that one's code may live much longer and be read by many more people than one would think while writing it. That awareness can be a great aid in writing cleaner, more elegant code, and in keeping "the bigger picture" in mind. I'm glad you didn't take my comment the wrong way - many people in, say, Java, would have - and totally agree with you about Smalltalkers caring enough to discuss pros and cons of tiny differences in coding styles.

Incidentally, I don't just read other people's blogs to make nitpicky comments about code ;-), I've actually been getting a lot out of your tutorials and explanations these past few days, trying to learn about Seaside, so keep it up, mate, great stuff!

Ramon Leon 3209 days ago

Appreciate a good comment anytime, especially the ones that make me think!

about me|good books|popular posts|atom|rss