Marcel Taeumel uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-mt.851.mcz ==================== Summary ==================== Name: Collections-mt.851 Author: mt Time: 2 September 2019, 11:43:25.156478 am UUID: f2b64324-98fe-5846-8e48-cfc0a1481b61 Ancestors: Collections-ct.850 Merges Collections-ct.829, which I moved to treated inbox due to incompatible ancestry. "Refactor Matrix instance creation: Matrix new returns a valid (empty) matrix instead of an uninitialized object" =============== Diff against Collections-ct.850 =============== Item was added: + ----- Method: Matrix class>>new (in category 'instance creation') ----- + new + ^self rows: 0 columns: 0! Item was changed: ----- Method: Matrix class>>rows:columns:contents: (in category 'private') ----- rows: rows columns: columns contents: contents + ^super new rows: rows columns: columns contents: contents! - ^self new rows: rows columns: columns contents: contents! |
Thank you :)
A very minor question: Would you favor [self basicNew] over [super new]? It will work completely equivalently, but what is the better style?
Christoph Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Montag, 2. September 2019 11:43:30 An: [hidden email]; [hidden email] Betreff: [squeak-dev] The Trunk: Collections-mt.851.mcz Marcel Taeumel uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-mt.851.mcz ==================== Summary ==================== Name: Collections-mt.851 Author: mt Time: 2 September 2019, 11:43:25.156478 am UUID: f2b64324-98fe-5846-8e48-cfc0a1481b61 Ancestors: Collections-ct.850 Merges Collections-ct.829, which I moved to treated inbox due to incompatible ancestry. "Refactor Matrix instance creation: Matrix new returns a valid (empty) matrix instead of an uninitialized object" =============== Diff against Collections-ct.850 =============== Item was added: + ----- Method: Matrix class>>new (in category 'instance creation') ----- + new + ^self rows: 0 columns: 0! Item was changed: ----- Method: Matrix class>>rows:columns:contents: (in category 'private') ----- rows: rows columns: columns contents: contents + ^super new rows: rows columns: columns contents: contents! - ^self new rows: rows columns: columns contents: contents!
Carpe Squeak!
|
> On 2019-09-04, at 12:58 PM, Thiede, Christoph <[hidden email]> wrote: > > Thank you :) > > A very minor question: Would you favor [self basicNew] over [super new]? It will work completely equivalently, but what is the better style? ^super new `self basicNew` will not always work equivalently. And I can almost guarantee it will always do the wrong thing just when you feel sure it is doing the right thing. And you will suffer Debugging Hell. tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Why use one word when two polysyllabic agglomerates will do? |
Hmm, I just starred at method 'source with it' in a SpurVMMaker image and got - 252 hits for 'self basicNew'; - 109 hits for 'super new'. So it might depend on context... This would deserve more statistics (age of method, initials, whether new is raising an Error or not, whether instance side initialize does nothing or the wrong thing, etc...) Le mer. 4 sept. 2019 à 22:38, tim Rowledge <[hidden email]> a écrit :
|
Non-scientific sampling, there are many places where new sends basicNew initialize, so the sender either wasn't aware new already did that, or didn't trust Behaviour>>new to keep doing that. Those can obviously just be fixed if desired. (Monticello code seems rife with this, interestingly). One specifically called out "Superclasses might [do something wrong]" to justify calling #basicNew in #new. Many callers are class side methods that intentionally setup the variables in subsequent calls to the instance created by #basicNew, such as DateAndTime. So, a mishmash of reasons. -cbc On Wed, Sep 4, 2019 at 2:08 PM Nicolas Cellier <[hidden email]> wrote:
|
In reply to this post by Nicolas Cellier
Well, to try to explain this mostly to Christoph, #basicNew should do only the primitive creation of the chunk of object memory, hence the 'basic' (and that does include some hacks for certain classes likeBoxedFloat etc).
#new and other initial creation methods can do further things, like setting initial values of some instance variables, registering external resources, etc. So they do not in general work equivalently, deliberately. At the most basic level, #new sends #basicNew to create the memory and then #initialize to do the ... initialisation. If that initialize does stuff your specific needs would prefer to avoid, use #basicNew and your own initialisation sequence. As an example, consider WriteStream>on:from:to: which uses `self basicNew`. It could use #new without any difference in effect because there is no implementation of #initialize for WriteStreams until you get to the null version in ProtoObject. In contrast, imagine a class where the normal use needs a load of instance variable to be set up just so, but you have a need for a quite different setup and it would waste a lot of cycles to do the typical init and then overwrite almost all of it. That might justify a separate creation method that uses #basicNew and a specialised init method. There's also the question of self vs super in your original question. Remember, super is for case where you need to skip up the class tree starting in the superclass of *the class in which the method you are running* lives. This is *not* always the super class of the object executing the method. Lots of people get this wrong. There are good explanations of the real effects of this crucial point in the Blue Book etc and you really need an example class tree with at least 4 level and it's too long to type here. Suffice to say that the practical difference between `self basicNew` and `super new` can be significant. > On 2019-09-04, at 2:07 PM, Nicolas Cellier <[hidden email]> wrote: > > Hmm, I just starred at method 'source with it' in a SpurVMMaker image and got > - 252 hits for 'self basicNew'; > - 109 hits for 'super new'. > > So it might depend on context... This would deserve more statistics (age of method, initials, whether new is raising an Error or not, whether instance side initialize does nothing or the wrong thing, etc...) > > Le mer. 4 sept. 2019 à 22:38, tim Rowledge <[hidden email]> a écrit : > > > > On 2019-09-04, at 12:58 PM, Thiede, Christoph <[hidden email]> wrote: > > > > Thank you :) > > > > A very minor question: Would you favor [self basicNew] over [super new]? It will work completely equivalently, but what is the better style? > > ^super new > > `self basicNew` will not always work equivalently. And I can almost guarantee it will always do the wrong thing just when you feel sure it is doing the right thing. And you will suffer Debugging Hell. > > > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Why use one word when two polysyllabic agglomerates will do? > > > > > > tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Computing Dictionary: LOOP: (go to LOOP) |
In reply to this post by cbc
The funny thing is that the object class (a Metaclass) is yet another object than the new instance. It
thus has absolutely no other way to control the construction of this
new instance, than to kindly ask the instance by sending a message
initialize. Even if it may sound more
imperative like object setThisTo: 1 andThatTo: Array new, only the
instance knows how it will do it (or just fake). Somehow the instance is thrown away un-initialized in the world, and we try to repair ASAP. I wonder how Newspeak resolved this. Systematic send of initialize in Behavior was a relatively late decision (not in Smalltalk-80), that's why I was speaking of method age: self basicNew initialize was the old way to do it indeed, left at discretion of classes when there were no generic initialize method in Object (ProtoObject). We can agree that super new is the canonical way to instantiate when additional behavior is required, with exceptions when super new or self initialize won't do the right thing. And instance side initialize should better super initialize too, generally. Le mer. 4 sept. 2019 à 23:58, Chris Cunningham <[hidden email]> a écrit :
|
In reply to this post by timrowledge
Okay, thank you for the really comprehensive clarification :)
I was already aware of the "basic" meaning of #basicNew, but I also met a pattern that told me to only use super to call the same-named base method. It feels like breaking with the law of encapsulation when you can always refer to the base version of a method that has been explicitly changed in the current class.
But I guess in this case, it is more important to deal with possible logic that could be introduced in (Proto)Object>>#initialize.
Christoph Von: Squeak-dev <[hidden email]> im Auftrag von tim Rowledge <[hidden email]>
Gesendet: Mittwoch, 4. September 2019 23:43:22 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Trunk: Collections-mt.851.mcz Well, to try to explain this mostly to Christoph, #basicNew should do only the primitive creation of the chunk of object memory, hence the 'basic' (and that does include some hacks for certain classes likeBoxedFloat etc).
#new and other initial creation methods can do further things, like setting initial values of some instance variables, registering external resources, etc. So they do not in general work equivalently, deliberately. At the most basic level, #new sends #basicNew to create the memory and then #initialize to do the ... initialisation. If that initialize does stuff your specific needs would prefer to avoid, use #basicNew and your own initialisation sequence. As an example, consider WriteStream>on:from:to: which uses `self basicNew`. It could use #new without any difference in effect because there is no implementation of #initialize for WriteStreams until you get to the null version in ProtoObject. In contrast, imagine a class where the normal use needs a load of instance variable to be set up just so, but you have a need for a quite different setup and it would waste a lot of cycles to do the typical init and then overwrite almost all of it. That might justify a separate creation method that uses #basicNew and a specialised init method. There's also the question of self vs super in your original question. Remember, super is for case where you need to skip up the class tree starting in the superclass of *the class in which the method you are running* lives. This is *not* always the super class of the object executing the method. Lots of people get this wrong. There are good explanations of the real effects of this crucial point in the Blue Book etc and you really need an example class tree with at least 4 level and it's too long to type here. Suffice to say that the practical difference between `self basicNew` and `super new` can be significant. > On 2019-09-04, at 2:07 PM, Nicolas Cellier <[hidden email]> wrote: > > Hmm, I just starred at method 'source with it' in a SpurVMMaker image and got > - 252 hits for 'self basicNew'; > - 109 hits for 'super new'. > > So it might depend on context... This would deserve more statistics (age of method, initials, whether new is raising an Error or not, whether instance side initialize does nothing or the wrong thing, etc...) > > Le mer. 4 sept. 2019 à 22:38, tim Rowledge <[hidden email]> a écrit : > > > > On 2019-09-04, at 12:58 PM, Thiede, Christoph <[hidden email]> wrote: > > > > Thank you :) > > > > A very minor question: Would you favor [self basicNew] over [super new]? It will work completely equivalently, but what is the better style? > > ^super new > > `self basicNew` will not always work equivalently. And I can almost guarantee it will always do the wrong thing just when you feel sure it is doing the right thing. And you will suffer Debugging Hell. > > > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Why use one word when two polysyllabic agglomerates will do? > > > > > > tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Computing Dictionary: LOOP: (go to LOOP)
Carpe Squeak!
|
To be absolutely thorough, also consider how the new: message is associated to basicNew: or super new: and initialize: for variable subclasses - or eventually just initialize: for those quaking like a variable subclass... Le jeu. 5 sept. 2019 à 00:27, Thiede, Christoph <[hidden email]> a écrit :
|
Hmm, got it wrong, it should read (basicNew: AND initialize:) OR super new: Le jeu. 5 sept. 2019 à 00:40, Nicolas Cellier <[hidden email]> a écrit :
|
In reply to this post by timrowledge
C'mon guys, it's neither! "Different super call" is a classic smell picked up by Lint, and I agree with Tim about "basicNew" -- is an unnecessary use of a low-level method. So why not simply the same conventional Smalltalky way, overriding #initialize like everywhere else? initialize super initialize. self rows: 0 columns: 0 contents: Array empty And, done..? - Chris On Wed, Sep 4, 2019 at 3:38 PM tim Rowledge <[hidden email]> wrote:
|
IMO, no. We don't want to have an empty Matrix by default, it's useless 99% of time. So it means initializing twice for nothing 99% of time. That's exactly where I would put basicNew... Le jeu. 5 sept. 2019 à 22:35, Chris Muller <[hidden email]> a écrit :
|
On Thu, Sep 5, 2019 at 3:43 PM Nicolas Cellier <[hidden email]> wrote:
But that's exactly what this commit is about -- having "Matrix new" return a well-formed (empty) Matrix. Isn't it what we're talking about?
"Initializing twice" is something we've accepted as an acceptable trade-off for nice code since 2006, when we changed Behavior>>#new to call #initialize. Via Array empty, we're talking about three measly variable assignments. Maybe it's just me, but that's light enough to prefer the nicer code. - Chris
|
Le jeu. 5 sept. 2019 à 23:52, Chris Muller <[hidden email]> a écrit :
Yes, but the 1% of case when this could be useful should not spoil the 99%.
The reason to put initialize in Behavior was to avoid hundreds of duplications of ^self basicNew initialize The trade off was to eventually call an empty initialize method for those not needing one Most classes take care to not double initialize when using alternate instance creation method. That's why they use basicNew/basicNew:. So we can have our cake and eat it too. Wasting does not fit the predominant definition of nice nowadays, at least not mine ;)
|
In reply to this post by Chris Muller-3
I just remembered your prior argument against using Array empty instead of creating one so, okay, an allocation would push it over the line. I can understand wanting extreme efficiency for very abstract classes like Point and Rectangle, so maybe Matrix is one of those. Best, Chris
|
In reply to this post by Chris Muller-3
Hi Chris. :-) > "Initializing twice" is something we've accepted as an acceptable trade-off for nice code since 2006, when we changed Behavior>>#new to call #initialize. Hmm... disagree. Slightly. I like that #new calls #initialize. Yet, it can always be a smell that can be corrected via "super new" or "self basicNew myCustomInitialize". There are several examples in the image of custom initializers that get called explicitely after #basicNew. Though, I prefer the #basicNew+#myCustomInitialize over "super new". Best, Marcel
|
Free forum by Nabble | Edit this page |