Bijan Parsia bparsia@email.unc.edu writes: (In Lisp): "The macro and evaluates each form one at a time from left to right. As soon as any form evaluates to nil, and returns nil without evaluating the remaining forms. If all forms but the last evaluate to true values, and returns the results produced by evaluating the last form." And that's *is* what #& does. However, Boolean>>&s *comment* states: "Evaluating conjunction. Evaluate the argument. Then answer true if both the receiver and the argument are true." Thus the contrast is supposed to be between a short circuting and non short circuting set of operators. That *is* precisely the contrast between & and and:. Note that the method comment for & says nothing about what happens if the receiver is not true or the argument is not true.
In fact True>>& alternativeObject 's comment says answer alternativeObject since receiver is true.
We find
True>> & alternativeObject ^alternativeObject and: alternativeBlock ^alternativeBlock value | aBoolean ^self or: alternativeBlock ^self
False>> & alternativeObject ^self and: alternativeBlock ^self | aBoolean ^aBoolean or: alternativeBlock ^alternativeBlock value
There is complete consistency here *except* that the argument of #| should be called alternativeObject, not aBoolean.
If (expr) is free of side effects, then x & (expr) x and: [(expr)] will have the same result, even if (expr)'s result is not a Boolean, and x | (expr) x or: [(expr)] will have the same result, even if (expr)'s result is not a Boolean.
For example, true & 3 => 3 true and: [3] => 3 false | 42 => 42 false or: [42] => 42
To stick a test in #& or #| without sticking a result test in #and: or #or: would be to *introduce* an inconsistency.
Side note, I don't like returning *whatever* the last item is, especially if it's not a boolean (the way lisp does). In the ANSI spec, the return value of #&/#| is supposed to be defined by the respective truth tables. Afaik, true and false are the only truth values in Smalltalk (the other objects do *not* have a Boolean interpretation) which suggests that true | 4 should be an error. Of course, IIRC, the ANSI spec sorta leaves this situation undefined, so I guess returning anything is kosher. It's really a speed/safety tradeoff. Do you want every and: and or: in the Smalltalk universe to run slower to catch type errors that don't really seem to be a problem?
On Tue, 20 Mar 2001, Richard A. O'Keefe wrote:
Bijan Parsia bparsia@email.unc.edu writes:
[snip]
Thus the contrast is supposed to be between a short circuting and non short circuting set of operators.
That *is* precisely the contrast between & and and:.
Not if by "evaluate the argument" you mean (as makes sense in Smalltalk) "sends #value to it". It also depends on what you mean by "argument".
true & self msg1 msg2 msg3
Is the argument to #& above the message chain "self msg1 msg2 msg3"? (I called this the syntactic argument.) Or is it the *result* of sending msg1 to self, msg2 to the result of that, and msg3 to the result of that? Ordinarily, the argument to #& would be the latter. That is, after all, what gets sent to true and is what's bound to the formal parameter of the method.
It's true that that message chain gets evaluated, so, in contrast to:
true & [self msg1 msg2 msg3]
the syntactic argument isn't short circuted (in the first example). Note that this is *not* a property of the #&. And *even if* the code block *would* return true if sent value, "&" will return *the code block*. Unevaluated. The unevaluated code block. Thus, #& does not evaluate its argument (for a reasonable sense of evaluate and argument).
Is this a problem? Dunno. I would avoid sending blocks to & (indeed, non-booleans) as a matter of personal preference and style. I would also make it clear that & doesn't evaluate the object passed as its argument (*even if* it's passed a boolean, since, for the some senses of evalute, it's not).
Note that the method comment for & says nothing about what happens if the receiver is not true
As it should not. If the receiver isn't true, we have a #doesNotUnderstand: unless your object or one of its superclasses implement #& (e.g., is a new subclass of True).
or the argument is not true.
Yes. And closer reading of the ANSI spec convinces me that the non-boolean argument case is undefined. So whatever! (But we do want to be sensible. Since every smalltalk I've investigated does things this way, I'm not agitating for change unless it's global, ANSI based change.)
But it *does* say that #& evalutates its argument. Boolean>>and: says:
"Nonevaluating conjunction. If the receiver is true, answer the value of the argument, alternativeBlock; otherwise answer false without evaluating the argument."
Thus, "evaluating" an argument, in context, is gettiing the value, that is sending #value (which is what True>>and: *does*.
Neither #& nor #| evaluate their argumetn.
In fact True>>& alternativeObject 's comment says answer alternativeObject since receiver is true.
Fine. This is all reasonable behavior, but it's not evaluating that alternativeObject.
Contrast with True>>and:'s comment: "Nonevaluating conjunction -- answer the value of alternativeBlock since the receiver is true." ^ alternativeBlock value
Now, the *nonevaluating* conjunction evaluates its argumetn while the *evaluating* conjection *doesn't*!! ("Nonevaluating" surely means "short circuting", which surely means (for conjunction) *evaluate* the second conjunct if and only if the first conjunct is true.
We find
True>> & alternativeObject ^alternativeObject and: alternativeBlock ^alternativeBlock value
There is the inconsistency. In #and:, "evaluate the argument" means "send #value to it", where is in #& it means "return it".
| aBoolean
^self or: alternativeBlock ^self
And here, the second argument isn't evaluated at all, and isn't expected to be (hence the "aBoolean"). *Mutatis mutandis* for False.
[snip]
There is complete consistency here *except* that the argument of #| should be called alternativeObject, not aBoolean.
Do you still think this? If so, we should go to private mail and I need an argument. Note that I'm *not* arguing that this bad semantics or changeworthy semantics, just ambiguously commented semantics.
If (expr) is free of side effects, then
Aren't the interesting cases the side effected ones? Oh well.
x & (expr) x and: [(expr)] will have the same result, even if (expr)'s result is not a Boolean,
But the relevant contrast cases are: x & [(expr)] x and: [(expr)]
Your case compares the evaluation of the *syntactic* argument. Fine, but no where else to we consider the *syntactic* argument to be the argument of a message. I recognize that we're doing a bit of operator/binary message punning here, but I don't think it's all that helpful.
[snip]
For example, true & 3 => 3 true and: [3] => 3
true & [3] => [] in UndefinedObject>>DoIt "I.e., the block"
Now, #and: is supposed to *evaluate* its argument iff the receiver is true. In your example, the receiver is true and #and: returns the *value* of the argument, to wit, 3.
#& (if "evaluating"/non-short circuting) is supposed to evaluate its argument (no matter what) and return true if both receiver and argument are true. I'll by pretending that a Boolean evaluates to itself, but if you passed a block with & to true to you *don't* get the value of that argument, you get the argument itself! That argument *never never* gets evaluated (in the sense of evaluation established by #and:).
Clearly, in practice, this hasn't been a big deal. But it's *surely* an inconsistency.
[snip]
To stick a test in #& or #| without sticking a result test in #and: or #or: would be to *introduce* an inconsistency.
1) I didn't advocate tests, or indeed, any change. 2) I would agree that that's an additional inconsistency to be avoided. Right now, you *can't pass a boolean* to #and:! That seems inconsistent with the "true *is*, for our purpuses, self-evaluating". Which seems to argue that #& is supposed to evaluate its *syntactic* argument. But #and: can do that too:
true and: self returnABlockAnsweringTrue
Now, this #and: evaluates both its syntactic argument, to get its blockAnsweringTrue, and its argument (to get true). For fun, try:
true and: [[true]] value
Oops, damn. #and: can't do either. That's because the *parser* catches it! Weird. It seems that it'll only allow a block or a not messaged object as its syntactic argument. Which means:
|temp| temp := [true]. true and: temp.
*will* work. Hmm.
Ok, I might be willing to concede that "evaluate" means something like "if whatever it was was bundled up in a block, what that block would return, but with non-evaluating #and:, we have to put the block in explicitly, but heck, the parser will help you keep that straight". Which, upon reflection, is how I used to understand it. Oops! :)
However, it's not bleeding obvious from the comments!
[snip]
It's really a speed/safety tradeoff. Do you want every and: and or: in the Smalltalk universe to run slower to catch type errors that don't really seem to be a problem?
Course not :) *bad hoc* [sic] test:
canc: alternativeBlock alternativeBlock value ifTrue: [^ true]
(Adds two bycodes.)
Time millisecondsToRun: [10000 timesRepeat: [true cand: [true]]] 125 Time millisecondsToRun: [10000 timesRepeat: [true and: [true]]] 25
If you drop out the ^true (which should have the same effect), you can shave that down to one extra bytecode and 111 milliseconds. So, around 5-fold difference.
How's this for some weird kool-aid:
Time millisecondsToRun: [10000 timesRepeat: [true & true]] 24 Time millisecondsToRun: [10000 timesRepeat: [true & [true]]] 60
Huh!! Strange. Returning the block is mucho expensive compared with returning its value (in the dummy case, of course).
Time millisecondsToRun: [10000 timesRepeat: [true and: [3]]] 17 Time millisecondsToRun: [10000 timesRepeat: [true & [3]]] 56
Wow, this holds true for a lot of simple operations (including float/integer addition). I wonder why.
Oh well, this note has gone on long enough :)
Cheers, Bijan Parsia.
P.S. I was looking at your book, the *Craft of Prolog* tonight! Good stuff. I hear your "email voice" as I read it :)
"Richard A. O'Keefe" ok@atlas.otago.ac.nz wrote:
Side note, I don't like returning *whatever* the last item is, especially if it's not a boolean (the way lisp does). In the ANSI spec, the return value of #&/#| is supposed to be defined by the respective truth tables. Afaik, true and false are the only truth values in Smalltalk (the other objects do *not* have a Boolean interpretation) which suggests that true | 4 should be an error. Of course, IIRC, the ANSI spec sorta leaves this situation undefined, so I guess returning anything is kosher.
It's really a speed/safety tradeoff. Do you want every and: and or: in the Smalltalk universe to run slower to catch type errors that don't really seem to be a problem?
It's not that simple. Besides speed and safety, all extra code means extra code to maintain and an extra chance to smake a mistake. In fact, I'd guess that extra checks like this don't tend to get tested very hard, and so it's easier for errors to slip through.
Many times, the fastest way to see whether an operation makes sense is to try it and see what happens. It's hard for me to imagine someone using & in a situation where both the first argument is *never* false and where the result of the & isn't actually checked. Almost always it's something like:
(..a...) & (..b...) ifTrue: [... ].
So the ifTrue: will catch the error if you ever run a test case with (..a..) being false.
Okay, so this is a mountain out of a 5-token molehill. But I wanted to defend the general principle, and say *don't* add explicit type checks!--
-Lex
squeak-dev@lists.squeakfoundation.org