Well, the last 16 posts or so have been about this, so that is hopefully clear by now. As for.
Your example A is defective because ~wahr
expects an argument, but you’re not providing one that makes sense from the 5.do
context. So i
in ~wahr
is going to be nil
in that example A call.
~wahr = {|i| ("wahr" + i).postln};
~wahr.() // wahr nil
What {~wahr.(i)}
does is to return a function that sends to ~wahr
the proper argument from the 5.do
context (which is also a function). So {~wahr.(i)}
is not the same function as ~wahr
because i
in the former is already bound; a clue is that {~wahr.(i)}
is a function that does not take any arguments! This is really called creating a closure.
Here’s a simpler example
~wahr = {|i| ("wahr" + i).postln};
z = {~wahr.(42)} // also a function
// but takes no arg and always gives the same arg to ~wahr
z.() // 42
z.(111) // still 42
Now every time the do
does a loop it creates a new function like z
, but with a different constant bound inside, from the current loop iteration.
Well, that is what happens conceptually. In reality, the if
there gets inlined
at line 13 below because you’ve used braces, i.e. functions on both branches of if
and because the two branch functions like {~wahr.(i)}
don’t declare any variables of their own! So the if
practically works like in a traditional imperative language in your case, meaning no actual functions get created from {~wahr.(i)}
.
{ if ( [false, true].choose, {~wahr.(i)}, {~falsch.(i)} ) }.def.dumpByteCodes
/*
BYTECODES: (34)
0 06 19 PushSpecialClass 'Array'
2 65 PushSpecialValue 2
3 C2 00 SendSpecialMsg 'new'
5 6D PushSpecialValue false
6 C2 08 SendSpecialMsg 'add'
8 6C PushSpecialValue true
9 C2 08 SendSpecialMsg 'add'
11 C1 24 SendSpecialMsg 'choose'
13 F8 00 0A JumpIfFalse 10 (26)
16 41 PushLiteral Symbol 'wahr'
17 C1 3C SendSpecialMsg 'envirGet'
19 1A PushInstVar 'i'
20 B0 TailCallReturnFromFunction
21 C2 06 SendSpecialMsg 'value'
23 FC 00 07 JumpFwd 7 (33)
26 40 PushLiteral Symbol 'falsch'
27 C1 3C SendSpecialMsg 'envirGet'
29 1A PushInstVar 'i'
30 B0 TailCallReturnFromFunction
31 C2 06 SendSpecialMsg 'value'
33 F2 BlockReturn
-> < closed FunctionDef >
*/
For the hardcore geeks, the magic stuff happens in compileIfMsg. The test for inlining isAnInlineableBlock
is indeed whether both branch functions in an if
declare no arguments and no variables of their own.