Lazy-initialization patterns

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

Lazy-initialization patterns

Esteban A. Maringolo
Hi there,

I have a question about lazy initialization accessor methods that always keep me thinking in terms of performance and style.

For years I did something like:

MyClass>>#property
  property isNil ifTrue: [ self initializeProperty ]. "It could be ifNil: as well"
  ^property

MyClass>>#initializeProperty 
  "Returns the receiver, not the property."
   property := self foo collect: [:e | ... ].


But this made me think that maybe I'm performing extra operations, so I started doing something like:

MyClass>>#property
  ^property ifNil: [ self initializeProperty ]

MyClass>>#initializeProperty
  ^property := self foo collect: [:e | ... ]

The initialize method is often created by an "Extract Method" refactoring I do in the accesor, where I do the initial implementation.


The messages makes me think that the second implementation is "faster", but an initializationMethod returning the value instead of the receiver is a code smell to me.

I early initialize everything I know will be be used right away, but for cases it doesn't make sense.


What do you think/prefer? Why?

Maybe we could use the reified variables (slots) to implement the lazy initialization themselves :)


Best regards!

Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

Paul DeBruicker
Seems like either way would have unmeasurable performance impact for your application vs the other and you should go for whats most legible/comprehensible.


On that metric I prefer the first but #yolo  






Esteban A. Maringolo wrote
Hi there,

I have a question about lazy initialization accessor methods that always
keep me thinking in terms of performance and style.

For years I did something like:

MyClass>>#property
  property isNil ifTrue: [ self initializeProperty ]. "It could be ifNil:
as well"
  ^property

MyClass>>#initializeProperty
  "Returns the receiver, not the property."
   property := self foo collect: [:e | ... ].


But this made me think that maybe I'm performing extra operations, so I
started doing something like:

MyClass>>#property
  ^property ifNil: [ self initializeProperty ]

MyClass>>#initializeProperty
  ^property := self foo collect: [:e | ... ]

The initialize method is often created by an "Extract Method" refactoring I
do in the accesor, where I do the initial implementation.


The messages makes me think that the second implementation is "faster", but
an initializationMethod returning the value instead of the receiver is a
code smell to me.

I early initialize everything I know will be be used right away, but for
cases it doesn't make sense.


What do you think/prefer? Why?

Maybe we could use the reified variables (slots) to implement the lazy
initialization themselves :)


Best regards!

Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

Peter Uhnak
In reply to this post by Esteban A. Maringolo
Personally I prever to avoid long lazy initializations, so I have it nice and tidy in a single method.

MyClass>>#property
        ^ property ifNil: [ property := self bar collect: #food ]

If I want to initialize the value in a separate method (typically for testing or reuse), then I prefer to avoid state mutation there and instead I return the result.

MyClass>>#property
        ^ property ifNil: [ property := self createNewProperty ]

MyClass>>createNewProperty
        "I won't touch property instance variable here, just create a new value and return it"
        ^ self bar collect: [ :e | e drink ]


> an initializationMethod returning the value instead of the receiver is a code smell to me.

For this reason I tend to name such methods createNewX, initializeX says to me "only mutate state and return self"

Peter

Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

Esteban A. Maringolo
In reply to this post by Paul DeBruicker
I know the compiler is smart enough in most cases, and even if it isn't, an extra send or two won't hurt me in the kind of computation I do.

But it's the style what "worried" me the most.

Let's use the first then :)


Esteban A. Maringolo

2017-08-05 19:18 GMT-03:00 Paul DeBruicker <[hidden email]>:
Seems like either way would have unmeasurable performance impact for your
application vs the other and you should go for whats most
legible/comprehensible.


On that metric I prefer the first but #yolo







Esteban A. Maringolo wrote
> Hi there,
>
> I have a question about lazy initialization accessor methods that always
> keep me thinking in terms of performance and style.
>
> For years I did something like:
>
> MyClass>>#property
>   property isNil ifTrue: [ self initializeProperty ]. "It could be ifNil:
> as well"
>   ^property
>
> MyClass>>#initializeProperty
>   "Returns the receiver, not the property."
>    property := self foo collect: [:e | ... ].
>
>
> But this made me think that maybe I'm performing extra operations, so I
> started doing something like:
>
> MyClass>>#property
>   ^property ifNil: [ self initializeProperty ]
>
> MyClass>>#initializeProperty
>   ^property := self foo collect: [:e | ... ]
>
> The initialize method is often created by an "Extract Method" refactoring
> I
> do in the accesor, where I do the initial implementation.
>
>
> The messages makes me think that the second implementation is "faster",
> but
> an initializationMethod returning the value instead of the receiver is a
> code smell to me.
>
> I early initialize everything I know will be be used right away, but for
> cases it doesn't make sense.
>
>
> What do you think/prefer? Why?
>
> Maybe we could use the reified variables (slots) to implement the lazy
> initialization themselves :)
>
>
> Best regards!
>
> Esteban A. Maringolo





--
View this message in context: http://forum.world.st/Lazy-initialization-patterns-tp4958897p4958901.html
Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.


Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

Esteban A. Maringolo
In reply to this post by Peter Uhnak
2017-08-05 19:21 GMT-03:00 Peter Uhnak <[hidden email]>:

> If I want to initialize the value in a separate method (typically for testing or reuse), then I prefer to avoid state mutation there and instead I return the result.
>
> MyClass>>#property
>         ^ property ifNil: [ property := self createNewProperty ]
>
> MyClass>>createNewProperty
>         "I won't touch property instance variable here, just create a new value and return it"
>         ^ self bar collect: [ :e | e drink ]
>
>
> > an initializationMethod returning the value instead of the receiver is a code smell to me.
>
> For this reason I tend to name such methods createNewX, initializeX says to me "only mutate state and return self"

I like this approach very much, it makes the accessor code concise and
separates the implementation of the property creation.

Thank you!

Esteban A. Maringolo

Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

Henrik Sperre Johansen
Esteban A. Maringolo wrote
2017-08-05 19:21 GMT-03:00 Peter Uhnak <[hidden email]>:

> If I want to initialize the value in a separate method (typically for testing or reuse), then I prefer to avoid state mutation there and instead I return the result.
>
> MyClass>>#property
>         ^ property ifNil: [ property := self createNewProperty ]
>
> MyClass>>createNewProperty
>         "I won't touch property instance variable here, just create a new value and return it"
>         ^ self bar collect: [ :e | e drink ]
>
>
> > an initializationMethod returning the value instead of the receiver is a code smell to me.
>
> For this reason I tend to name such methods createNewX, initializeX says to me "only mutate state and return self"

I like this approach very much, it makes the accessor code concise and
separates the implementation of the property creation.

Thank you!

Esteban A. Maringolo
+1.
It also avoids having to dive a level deeper in a debugger when you want to inspect the current value vs initial value without modifying state.

For naming, I tend to use one of several depending on the use;
a) initialX
b) defaultX
c) calculateX
depending on whether the property is
a) expected to change value during the lifetime of the object
b) have an optional value provided at creation, then be static
c) always be calculated (ie, an instvar used to cache expensive results used often)

Cheers,
Henry
Reply | Threaded
Open this post in threaded view
|

Re: Lazy-initialization patterns

EstebanLM

On 7 Aug 2017, at 10:50, Henrik Sperre Johansen <[hidden email]> wrote:

MyClass>>#property
       ^ property ifNil: [ property := self createNewProperty ]

MyClass>>createNewProperty
       "I won't touch property instance variable here, just create a new
value and return it"
       ^ self bar collect: [ :e | e drink ]

I favour this approach :)
and… I consider it the “standard approach” for what goes into Pharo itself. 

Esteban