Self and Mutually Tail Recursive Functions
A function can be both self and mutually tail recursive and both of the optimizations shown above would be applied in that case.
Consider this Idris function, for example, where it calls itself as well as another function which calls back:
mutual
isEven : Nat -> Bool
isEven Z = True
isEven (S k) = isOdd k
isOdd : Nat -> Bool
isOdd Z = False
isOdd (S (S k)) = isOdd k
isOdd (S k) = isEven k
Decompiled Java code
The JVM bytecode for this function will have a loop and trampoline as we can see in the following decompiled code. Here the last default block has TcContinue constructor in one branch and overwrites argument value in the other branch for next iteration.
public static Object isOdd$tc2(Object arg$0) {
while(true) {
BigInteger constantCaseExpr0 = (BigInteger)arg$0;
int hashCodePosition1 = -1;
switch (constantCaseExpr0.hashCode()) {
case 0:
if (constantCaseExpr0.equals(BigInteger.ZERO)) {
hashCodePosition1 = 0;
}
default:
switch (hashCodePosition1) {
case 0:
return new TcDone(0, 0);
default:
Object e$0 = ((BigInteger)arg$0).subtract(BigInteger.ONE);
BigInteger constantCaseExpr2 = (BigInteger)e$0;
int hashCodePosition3 = -1;
switch (constantCaseExpr2.hashCode()) {
case 0:
if (constantCaseExpr2.equals(BigInteger.ZERO)) {
hashCodePosition3 = 0;
}
default:
switch (hashCodePosition3) {
case 0:
return new TcContinue_1(1, e$0);
default:
Object e$1 = ((BigInteger)e$0).subtract(BigInteger.ONE);
arg$0 = e$1;
}
}
}
}
}
}