What is a young Smalltalker to do? SmallLint forcefully suggests that:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new]
should be rewritten as:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new] ifFalse: [nil]
which would be fine, but as soon as I try and pretty print the rejigged method, it reverts to the version without the ifFalse: clause?
Two tools enter, one tool leaves. But which one?
On Tue, 26 Jun 2007 13:24:39 -0700, Piers Cawley pdcawley@bofh.org.uk wrote:
What is a young Smalltalker to do? SmallLint forcefully suggests that:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new]
should be rewritten as:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new] ifFalse: [nil]
which would be fine, but as soon as I try and pretty print the rejigged method, it reverts to the version without the ifFalse: clause?
The latter strikes me as silly. In a language where "if" is part of the grammar, I can see it sort-of making sense, especially if nesting "if"s, to include "else"s with NOPs.
On Tue, 26 Jun 2007 13:45:22 -0700, Blake blake@kingdomrpg.com wrote:
The latter strikes me as silly. In a language where "if" is part of the grammar, I can see it sort-of making sense, especially if nesting "if"s, to include "else"s with NOPs.
The issue is, what does the expression return, in the false case, if #ifFalse: isn't there?
It may be a Smalltalk standard to return nil in that case - I have no idea. Personally, when I'm doing something with an expression (like returning it, or assigning it), I always make it very explicit what I (the programmer) expect to happen in all cases...
Later, Jon
-------------------------------------------------------------- Jon Hylands Jon@huv.com http://www.huv.com/jon
Project: Micro Raptor (Small Biped Velociraptor Robot) http://www.huv.com/blog
On Tue, 26 Jun 2007 14:00:36 -0700, Jon Hylands jon@huv.com wrote:
On Tue, 26 Jun 2007 13:45:22 -0700, Blake blake@kingdomrpg.com wrote:
The latter strikes me as silly. In a language where "if" is part of the grammar, I can see it sort-of making sense, especially if nesting "if"s, to include "else"s with NOPs.
The issue is, what does the expression return, in the false case, if #ifFalse: isn't there?
It may be a Smalltalk standard to return nil in that case - I have no idea. Personally, when I'm doing something with an expression (like returning it, or assigning it), I always make it very explicit what I (the programmer) expect to happen in all cases...
Good point. I didn't note the ^ at the beginning and was just looking at the statment.
However, is the question not really "What do the booleans return from an if* statement if the conditional is not met"? In which case, the answer is "nil".
Which, perhaps inevitably, leads me back to my original conclusion: It's silly. "if" is not a language construct with undefined and potentially ambiguous results. It's a set of messages sent to a class. It's kind of like all those languages that use "for" loops in which the control variable is undefined after the loop. Lint tools will warn you about using that control variable; that would just be silly in Smalltalk.
I guess if we're talking portability though, it might be a valid question. I'm not sure the Smalltalk spec mandates that instances of True and False must return nil. But what else =would= they return? Self?
===Blake===
On Jun 26, 2007, at 23:59 , Blake wrote:
However, is the question not really "What do the booleans return from an if* statement if the conditional is not met"? In which case, the answer is "nil".
I guess if we're talking portability though, it might be a valid question. I'm not sure the Smalltalk spec mandates that instances of True and False must return nil.
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
- Bert -
Bert Freudenberg wrote:
I guess if we're talking portability though, it might be a valid question. I'm not sure the Smalltalk spec mandates that instances of True and False must return nil.
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
That is not necessarily the case. One can make an equally good argument saying that "foo ifTrue:[...]" should expand to "foo ifTrue:[...] ifFalse:[foo]" which is coincidentally true for ifNil:ifNotNil: and *should* be true (and I'm glad we fixed this in Croquet) for ifEmpty:ifNotEmpty:. In other words if it is the case that:
42 ifNil:[...] => 42 #(1 2 3) ifEmpty:[...] => #(1 2 3)
then it seems quite consistent to have
false ifTrue:[...] => false.
(not that I'm proposing to change this btw, since it would break a whole bunch of stuff but it's perfectly consistent with other semantics that are generally deemed "intuitive")
Cheers, - Andreas
On Jun 27, 2007, at 9:02 , Andreas Raab wrote:
Bert Freudenberg wrote:
I guess if we're talking portability though, it might be a valid question. I'm not sure the Smalltalk spec mandates that instances of True and False must return nil.
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
That is not necessarily the case. One can make an equally good argument saying that "foo ifTrue:[...]" should expand to "foo ifTrue:[...] ifFalse:[foo]" which is coincidentally true for ifNil:ifNotNil: and *should* be true (and I'm glad we fixed this in Croquet) for ifEmpty:ifNotEmpty:. In other words if it is the case that:
42 ifNil:[...] => 42 #(1 2 3) ifEmpty:[...] => #(1 2 3)
then it seems quite consistent to have
false ifTrue:[...] => false.
(not that I'm proposing to change this btw, since it would break a whole bunch of stuff but it's perfectly consistent with other semantics that are generally deemed "intuitive")
Granted. I understood the question to be about wether the behavior was arbitrary and Squeak-specific or actually specified and portable. I'd argue it's the latter, if only because ifTrue:/ifFalse: predates the other constructs by a large margin and all Smalltalk variants inherited that behavior.
- Bert -
On Wed, 27 Jun 2007 00:02:46 -0700, Andreas Raab andreas.raab@gmx.de wrote:
ifEmpty:ifNotEmpty:. In other words if it is the case that:
42 ifNil:[...] => 42 #(1 2 3) ifEmpty:[...] => #(1 2 3)
then it seems quite consistent to have
false ifTrue:[...] => false.
(not that I'm proposing to change this btw, since it would break a whole bunch of stuff but it's perfectly consistent with other semantics that are generally deemed "intuitive")
Given that we're off in alternate realities, this probably isn't that important but wouldn't the above require a lot of code like this to be written:
^someTest ifTrue:[someObject] ifFalse: [nil].
because otherwise the return value will be a valid non-nil object that must be tested for. In other words, unless you set the result to nil specifically, clients of the code must be prepared to handle a valid object, nil (as now), and a boolean. And they must have awareness of the fact that sometimes True means nil and sometimes False means nil (or I guess that a boolean means nil). And then what if the non-nil block returns a boolean?
Not that such would be insurmountable, but it seems like the approach as implemented is cleaner.
===Blake===
On Wed, 27 Jun 2007 00:26:55 -0700, Blake blake@kingdomrpg.com wrote:
Given that we're off in alternate realities, this probably isn't that important but wouldn't the above require a lot of code like this to be written:
^someTest ifTrue:[someObject] ifFalse: [nil].
Which, I guess, now that Bert re-points it out, is exactly where we started.
Heh.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Andreas Raab schrieb:
That is not necessarily the case. One can make an equally good argument saying that "foo ifTrue:[...]" should expand to "foo ifTrue:[...] ifFalse:[foo]" which is coincidentally true for ifNil:ifNotNil: and *should* be true (and I'm glad we fixed this in Croquet) for ifEmpty:ifNotEmpty:. In other words if it is the case that:
42 ifNil:[...] => 42 #(1 2 3) ifEmpty:[...] => #(1 2 3)
I had above expectation for ifEmpty: once and got so badly burned, that I chickened out of using ifNil:, ifEmtpy: and their homies completely.
Alex
Hi andreas
That is not necessarily the case. One can make an equally good argument saying that "foo ifTrue:[...]" should expand to "foo ifTrue:[...] ifFalse:[foo]" which is coincidentally true for ifNil:ifNotNil: and *should* be true (and I'm glad we fixed this in Croquet) for ifEmpty:ifNotEmpty:. In other words if it is the case that:
42 ifNil:[...] => 42 #(1 2 3) ifEmpty:[...] => #(1 2 3)
Can you elaborate more on why it should be true for ifEmpty:?
then it seems quite consistent to have
false ifTrue:[...] => false.
(not that I'm proposing to change this btw, since it would break a whole bunch of stuff but it's perfectly consistent with other semantics that are generally deemed "intuitive")
Cheers,
- Andreas
On Tue, 26 Jun 2007 23:54:39 -0700, Bert Freudenberg bert@freudenbergs.de wrote:
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
So, lint is in this case un-short-handing.
On Jun 27, 2007, at 9:14 , Blake wrote:
On Tue, 26 Jun 2007 23:54:39 -0700, Bert Freudenberg bert@freudenbergs.de wrote:
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
So, lint is in this case un-short-handing.
Right. As Andreas points out it is conceivable that some Smalltalk would redefine the meaning so this would be more portable, and it might be considered more readable because it spells out the other case explicitly, leaving no one to wonder about the return value.
- Bert -
On Jun 27, 2007, at 9:14 , Blake wrote:
On Tue, 26 Jun 2007 23:54:39 -0700, Bert Freudenberg bert@freudenbergs.de wrote:
"ifTrue: aBlock" is short-hand for "ifTrue: trueBlock ifFalse: []". The value of an empty block is nil by definition. That's why True and False must return nil in #ifFalse: and #ifTrue:, respectively.
So, lint is in this case un-short-handing.
Right. As Andreas points out it is conceivable that some Smalltalk would redefine the meaning so this would be more portable, and it might be considered more readable because it spells out the other case explicitly, leaving no one to wonder about the return value.
I may be the one responsible for the shorthand here (but memory does not serve in this case). In any event, I'll add that I agree with the lint point of view that any of these paired expressions should include the nil branch when evaluated for value (as in assignments, as arguments, and as return values). The decompiler can recognize these cases, so I'd certainly agree with modifying the decompiler accordingly. When only evaluated for effect, the shorthand is not only simpler, but it invariably matches the program as originally written, so that should be retained.
- Dan
Someone wrote a package where you could allow specific "violations" for specific methods declared (I think) on the class side so that Lint would then ignore it going foward.
I can't remember who did it, but it was in the last few years. You'd have to search the archives to find it..
On 6/26/07, Piers Cawley pdcawley@bofh.org.uk wrote:
What is a young Smalltalker to do? SmallLint forcefully suggests that:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new]
should be rewritten as:
^ Preferences optionalButtons ifTrue: [OBFixedButtonPanel new] ifFalse: [nil]
which would be fine, but as soon as I try and pretty print the rejigged method, it reverts to the version without the ifFalse: clause?
Two tools enter, one tool leaves. But which one?
squeak-dev@lists.squeakfoundation.org