You've seen in the last sessions that recursion is a fundamental tool for functional programming. Whereas in an imperative language, you would probably rather use a loop like a while loop or a for loop. Now it turns out that recursion and loops have a lot to do with each other, and there is a form of recursion that is essentially exactly the same as a loop, so that form of recursion is called tail recursion, and we're going to find out more about it in this session. Before we do that, let's review again how we treat function applications in our evaluation model. There's one simple rule. One evaluates a function application f applied to arguments e_1 to e_n, by evaluating the expressions resulting in the values v_1 to v_n, and then replacing the application with the body of the function f, in which the actual parameters values, v_1 to v_n replace the former parameters of f. Now that notion is so important that it warrants to have a notation for a specialized notation. We can visualize that by a rewriting of the program itself. Here we would have a program that has a definition of the function f with these parameters. I've left out the types and the body. Here we have a call of the same function. That program can be rewritten to a program with the same definition and the same dot dot dot in-between. The filler in between here. But the call here would be replaced by the body with the substitution where every formal parameter x gets replaced by the corresponding value v. This notion with this in brackets, v_1 slash x_1, v_n slash x_n. That means precisely the expression B, where all occurrences of an x _i have been replaced by the corresponding v_i, and that whole block is called a substitution, and that's where the name substitution model comes from because essentially that's a core step of evaluation in that model. Let's do an example of this rewriting. We consider new function GCD, or greatest common divisor. The greatest common divisor of two numbers is the greatest number that divides both a and b. There's a very old algorithm, goes back to Euclid in the antiquities. That algorithm is like this. It says, well, if the second number is zero, then it's the first, and otherwise, you call GCD again with the first argument is now the second argument b. The second argument is the first argument, a Modulo b. That operator here, the percentage is modulo operator in Java and also in Scala. Now if we have a call GCD 14,21, you can visualize its evaluation as follows. We start with the call, then we expanded to the right-hand side of GCD. That would be this one here. With the correct parameters. We simplify the condition to false. We simplify the if, then else. If, since it's false, it's the else part, so it's this one here. We go on into the next call. Now we simplify the argument, 14 Modular 21 is 14, and we go on, we expand to the call. We simplify the call, so that's still false. It's the second GCD. We simplify that as well. We arrive at this GCD 14, 7, and so on. Finally, we will arrive at GCD 7, 0, and that means 7. This double arrow here means reduction by several steps. I've left out some steps between here and here, but it should be clear what they are. Now that we've seen that, let's try another function. Consider a factorial. The factorial function is defined like this. It takes an n Int and it gives you an integer result. It says if the argument is zero, then the factorial of zero is one. Otherwise the factorial is the number n times factorial n minus 1. Let's consider factorial 4. That would be the body of factorial that we see here. There are really two reduction steps. One is the body here, and finally that one reduces to 4 times factorial 3, the right-hand side here. If you go on then factorial 3 reduces to 3 times factorial 2, that reduces to 2 times factorial 1. Finally, we have 1 times factorial 0, and finally we have the argument equals 0, so that would give us 1. Then finally, the whole reduction will go back to 24. Do you see a difference between the two reduction sequences, the one GCD and the one factorial? If we look at the reduction sequence for GCD, we see it's essentially flat, so it will oscillate between if then elses and recursive calls, but essentially the expression we reduced to always follows the arrow immediately. Whereas if we see the reduction sequence for factorial, then we notice that we have deeper and deeper nesting, so we have essentially something that we have no already in some operands, four, three, two, and then the recursive call factorial. Here we reach four times level of nesting and then it unravels again until we have 24. Why is that? Why do we have the difference between those two reduction sequences? Well, if we look at GCD, then we see that GCD calls itself recursively as its last action. There's nothing after the recursive call. Recursive call is at the same time the result of GCD. Whereas if we look at factorial, then we noticed that after the call, there's still work to do. We still have to multiply with n. Here the call is not the last action of the body of the function. That leads to the different reduction traces. If the call is the last action, then essentially the intermediate expression in the reduction has always about the same size, whereas if the call is not the last thing then we essentially goes deeper and deeper. That observation which we have done on a very high level and on a very abstract level on the reduction traces, translates directly to an observation on the actual implementation hardware, because for function calls we have things called stack frames in a call stack. If a function calls itself as the last action, that stack frame can be reused, we can immediately use the same stack frame for the recursive call, and that is called tail recursion. Tail-recursive functions are in essence iterative processes. There's nothing different between a while loop and a tail-recursive function. In general, if the last action of a function consists of calling another function or maybe the same one, then we only need one stack frame for both functions. Such calls are called tail calls. In Scala, because it runs on the JVM, we can only optimize directly recursive calls to the current function. Such an optimization can help runtime speed of the function, and it also can avoid stack overflows. Turns out that in a normal stack on the JVM, you have maybe 2,000, 3,000 recursive calls until you get a stack overflow. If your programs are large enough and your data structures are large enough that isn't sufficient. It's very important that your calls are in fact tail calls. You can in fact know that at compile-time without running the program, because you can tell the compiler that you require that the function must be tail call optimizable, and you do that with an annotation which is called tailrec. You import it from Scala annotation tailrec, and then you can use tailrec. That means that if the implementation of GCD for some reason is not tail-recursive, then the compiler will tell you, it will give you a warning that says this function is declared tailrec, but it's actually not tail-recursive. On the other hand, even if you leave tailrec out, to compiler will still optimize your GCD function to be tail-recursive when it can. Basically tailrec is not a command that tells the compiler it must optimize. Will optimize, in any case, is just essentially a check for you to know that the compiler could actually do that in the end. An exercise for you, open-ended, you've seen factorial, and we've seen that it's not tail-recursive. Can you design a tail-recursive version of factorial?