Computers evaluate expressions reverse-Polish style. Yours translates to:
- Push function
{ false.debug(\1) }
.
- Value it – because of
.()
.
- Push function
{ true.debug(\2) }
.
- Value it – because of
.()
.
- Do
&&
on the two resulting values.
So, in your specific example, the reason why the second function evaluates it is because you explicitly requested it.
TL;DR the best ways to write AND and OR in SC are:
false.debug(\1) and: { true.debug(\2) }
false.debug(\1) && { true.debug(\2) }
Both do short-circuit the second function.
C-style short-circuiting of &&
and ||
is compiler magic. &&
translates to: If the first operand is 0, skip over the second operand (and ||
is, skip it if the first is nonzero). This means &&
and ||
are a hybrid of a math operator and a control structure.
But, as noted in my last post, in SC’s Smalltalk roots, conceptually there aren’t any control structures – only method dispatch and first-class functions. In practice, there are a few control structures because method dispatch is slower than “jump” byte codes.
“There aren’t any control structures” is a radical, mindbending idea for anyone raised on Pascal / C like I was… but:
// in the True class
and { arg that; ^that.value }
// in the False class: here's the runtime short-circuit!
and { arg that; ^this }
There’s no compiler magic here. Short-circuiting happens as a result of dispatching based on the first Boolean value (and depends on the second operand being a function – which is consistent with the general rule in SC that conditional or looped evaluation always requires passing a function somewhere, unlike C where some things that are not written as blocks behave as blocks, e.g. &&
second operand).
BTW, of “no control structures,” you might think while
is impossible without a jump-back instruction, but:
+ Function {
recursiveWhile { |body|
if(this.value.not) { ^nil };
body.value; // edit here: first version was wrong
^recursiveWhile(this, body) // missed the recursive call the first time
}
}
With early method exit and tail-call optimization (which SC has), while
can be done recursively. This is slooooowwwww but drives home that conceptually, compiler control structures are not strictly required at all. Compiler control structures in SC are purely performance optimizations. There’s no other reason for them.
SC tries to look a bit like C, but it isn’t C.
I would just add elements incrementally in that case.
hjh