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;
                                }
                        }
                }
        }
    }
}