Tail Calls
A novel feature of the implementation of dfns is the way in which tail calls are optimised.
When a dfn calls a sub-function, the result of the call may or may not be modified by the calling function before being returned. A call where the result is passed back immediately without modification is termed a tail call.
For example in the following, the first call on function fact is a tail call because the result of fact is the result of the whole expression, whereas the second call isn't because the result is subsequently multiplied by ⍵.
(⍺×⍵)fact ⍵-1 ⍝ Tail call on fact. ⍵×fact ⍵-1 ⍝ Embedded call on fact.
Tail calls occur frequently in dfns, and the interpreter optimises them by re-using the current stack frame instead of creating a new one. This gives a significant saving in both time and workspace usage. It is easy to check whether a call is a tail call by tracing it. An embedded call will pop up a new trace window for the called function, whereas a tail call will re-use the current one.
Using tail calls can improve code performance considerably, although at first the technique might appear obscure. A simple way to think of a tail call is as a branch with arguments. The tail call, in effect, branches to the first line of the function after installing new values for ⍵ and ⍺.
Iterative algorithms can almost always be coded using tail calls.
In general, when coding a loop, we use the following steps; possibly in a different order depending on whether we want to test at the "top" or the "bottom" of the loop.
- Initialise loop control variable(s). ⍝ init
- Test loop control variable. ⍝ test
- Process body of loop. ⍝ proc
- Modify loop control variable for next iteration. ⍝ mod
- Branch to step 2. ⍝ jump
For example, in classical APL you might find the following:
∇ value←limit loop value ⍝ init [1] top:→(⎕CT>value-limit)/0 ⍝ test [2] value←Next value ⍝ proc, mod [3] →top ⍝ jump ∇
Control structures help us to package these steps:
∇ value←limit loop value ⍝ init [1] :While ⎕CT<value-limit ⍝ test [2] value←Next value ⍝ proc, mod [3] :EndWhile ⍝ jump ∇
Using tail calls:
loop←{⍝ init ⎕CT>⍺-⍵:⍵ ⍝ test ⍺ ∇ Next ⍵ ⍝ proc, mod, jump }