Lazy-initialization patterns
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
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.
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 <pdebruic@gmail.com>:
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.
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
2017-08-05 19:21 GMT-03:00 Peter Uhnak <i.uhnak@gmail.com>:
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
Esteban A. Maringolo wrote
2017-08-05 19:21 GMT-03:00 Peter Uhnak <
i.uhnak@
>:
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 -- View this message in context: http://forum.world.st/Lazy-initialization-patterns-tp4958897p4959054.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
On 7 Aug 2017, at 10:50, Henrik Sperre Johansen <henrik.s.johansen@veloxit.no> 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
participants (5)
-
Esteban A. Maringolo -
Esteban Lorenzano -
Henrik Sperre Johansen -
Paul DeBruicker -
Peter Uhnak