Over the first two weeks we have introduced a model of evaluation of programs based on term rewriting. That was the so-called substitution model. It's a fair question to ask whether when we have now these new concepts, classes and methods and self-references, whether these concepts can be ported to our evaluation model. Whether they can be extended to cover these as well. In this session you're going to find out how this is done. So far we've only developed this model for pure functions. We have defined the meaning of a function application using a model based on substitutions. And now we extend this model to classes and objects. The first question we need to ask is how is an instantiation of a class like new C with some argument expression evaluated. The answer is surprisingly simple. We will evaluate the expression arguments, e 1 to e m. Just like the arguments of a normal function. And that's it. In fact the resulting expression new c of some values, v one to v m is already a value. So we just take these new instance creation expressions as values. Next question. Let's suppose we have a class definition, like the one you see here. Where we have a class C with a method F. And the formal parameters of the class are named X1 to XM. And the method F has parameters Y1 to YM. The function parameters or the class parameters can both be empty. And for simplicity we have omitted any parameter types. Now the central question is if we have an expression like that. We have a class instance new C of V1 to VM, and we call them F at F, and pass it further arguments values, W1 to WN. How is that evaluated? Let's see how we would answer these questions. So as a reminder we would have the class, C, and that has the X parameters. And then it has a function f. And that would have the y parameters. And a body B. If you have an expression like this one here, then it is re-written not using one substitution as for plain functions, but three substitutions. The first substitution is the one you have seen before. We would have to replace the actual, the formal parameters of the function f with the actual argument values w1 to wn, so that's that substitution that you see here. The next substitution affects the class. So we have former parameters of the class, this also have to be replaced by the actual argument values that we have here when we create the class. So that it gives us the second substitution. The third substitution is important, because the body of the function F here could contain reference to this, the current class itself and of course that reference to this outside of the class wouldn't have any meaning. So we have to replace it with something else. What do we replace it with? Well, the idea is simply the receiver of this call. So the value new C v1 to vm itself, so that's the object that takes the method call and so that's the object that replaces "this". So you see, there's three substitutions at play. It's quite a bit more complicated than in the purely functional model, but it's still the same model so we can model evaluation with exactly the same mechanisms as before. So let's demonstrate that with an example. We're looking at the method called new Rational 1, 2 dot numer. As a reminder, I've put up the essential parts of class rational here. So let's see what happens for this method call here. I put up the formal definition. We have three substitutions at play. There's a substitution for the class parameters where the actual values one, two replace the x and the y. For the method in this case, there's nothing to replace because numer doesn't have any parameters. And finally, any occurrence of this would have to be replaced by the object itself so that we would be new Rational(1, 2). In this case, it turns out that the right hand side of numer is very simple, it's just x. So the only piece of this substitution that applies is the left most one here. One for X and the result is one. Let's do a more complicated example, let's see whether new Rational of one two is less than new Rational of two three. How would we go about that? So here you see the definition of less. So we would have three substitutions again. There's the substitution of one and two for x and y, as before. Then there's the substitution of the argument here, a new rational of two, three for the That parameter of Less. And finally, there's a substitution that replaces the "this" in less's body by the receiver of the call, new Rational of (1, 2). And all these three substitutions are applied to the body of less that you see here. So here, it's written out in slightly nicer fonts. But that's the body that you see here. Note that I have made explicit that this is the argument for references because we need that for the substitution model to work correctly. So what that means is that, if I apply the substitutions, then the "this" gets replaced by new Rational (1, 2). So that's the two occurrences you see here. And the "that" gets replaced by new Rational (2, 3). That's the two occurrences that you see here. And if we then apply the further substitutions for numer, and an analogous substitution for denom, then we arrive at this expression here, which reduces to true, as usual. So one more thing we want to cover this session is operators. You see, in principle the rational numbers defined by our class are as natural as integers, and mathematical abstractions just as good as integers. But for the user of these abstractions there's currently a noticeable difference. You write if x and y are integers, you write x plus y. But if R and S are rational numbers, you need to write r.add(s). So that's not very natural, and in fact, in Scala we can eliminate this difference. To do that, we proceed in two steps. At the first step, we introduce in fix notation for methods. It turns out that any method with a parameter can be used like an infix operator in Scala. So instead of having to write r.add(s), you can write just as well, r add s. R dot less s becomes r less s, and for maximum it's the same thing. So the two things mean exactly the same things. The left sides expand to the right sides. You can do that for any operator that you have in Scala. The second step is about relaxing the form of identifiers. Normally in programing languages, identifiers are alpha numeric, so they start with a letter, then they are followed by a sequence of letters or numbers. In Scala you have an alternative form of identifiers where identifiers can be symbolic. They start with an operator symbol, such as plus or minus, anything other than a letter or a digit, and they're followed by other operator symbols. In this treatment, the underscore character underscored that we use a relaxed notion of identifiers in Scala. Normally in programming languages, an identifier is alpha numeric. It starts with a letter and it's followed by a sequence of letters and numbers. In Scala, operators are also treated as identifiers, and to achieve that we have introduced a second form of identifiers which we call symbolic. Such an identifier starts with an operator symbols such as plus, or minus or question mark, and is be followed by other operator symbols. In the definition, the underscore character, as usual, counts as a letter. And there's a final twist, we can also mix alphanumeric and symbolic. We can start with an alphanumeric identifier, followed by an underscore, then followed by some operator symbols. So here I give you some identifiers in Scala. x1, times, +?%&, that's not necessarily an identifier I would recommend you use, vector underscore ++ or counter underscore =. And all these names are legal identifiers in Scala. So let's see how we could apply that to class rational. Let's start with the first operator, less here. So a better name for less would be just the less than or equal sign. Then we would change max accordingly. To say if this less than that, and the program here compiles again. And the usage of course, it would be the same thing. I would write X less than Y. For maximum, I think maximum is a nice operator as it stands, but I can write it infix. Now that we've dealt with less and max. What about add and sub? So for add, we would, of course, find plus. For sub, we would, find minus. And the definition of sub would be this plus that dot neg. And in the code here, I can now use the arithmetic operations as I'm used from mathematics. Now the final operations to look at would be neg. You've seen that we can replace all the arithmetic operations on rational by the natural mathematical symbols. But there's still neg which sort of sticks out because, instead of neg, we would want to write prefix minus. So how can we achieve that? So if you look at the error message here, it tells us, well, unary underscore minus is not a member of rational. So what that means is that, because the prefix operator minus is really different from the infix operator minus, there's a special convention in Scala. We have to call it unary minus. And if we do that, then, we see that, now everything works out, and we also get prefix operators. One thing to guard against is that if a method name ends in an operator symbol, then you need at least a space between that and the final colon. Because otherwise, a colon is actually a legal symbol. So the colon would just be merged with the operator name, to form one long operator minus colon. So that would be here, an error, that you see. So, you've seen that we can now use the usual mathematical expressions like, x times x plus y times y. But, there's one issue still, namely about the precedence of the operators. As you're used, the times here binds stronger than the plus, so implicit parenthesis go like this. But you might ask, well how is that actually achieved if all operators are user defined, how is the precedence between those operators established? Well, there's actually one universal rule in Scala, that the precedence of an operator is determined by its first character. And here's a table of all the characters and the precedence categories. You will note that the table is very similar to precedence in a language such as Java or C, and that's no accident. So, the first thing to do is to say, well the lowest precedence is every operator that starts with a letter. So alphanumeric operators have lowest precedence. Then we take essentially the precedence groups of C and Java. So the next lowest is bar, followed by caret, followed by ampersand, followed by less than, greater than, equals, bang. The colon is inserted the colon is not in java so we inserted in here then plus minus and finally times, slash and percent. And every character which not in this list, every symbol character which is not in this list is assumed to have a higher precedence than everything else. You might say why these rules and not others and I don't really have a good answer for that. The only answer is that it's important that everybody uses the same precedence rules because otherwise it's very hard to read other people's code. So that's why Scala has opted for a single rule that determines the precedence of each operator. So as an exercise for this session, I would like you to ask to provide a fully parenthesized version of this string of this expression here. What I mean by that is that you should put every binary operation into parenthesis in a way that the structure of the expression doesn't change. So let's see how we would go about that. In this string here, what's the operator with the highest precedence? Well it turns out it's the ?^ because question mark was not in our list yet it's a symbolic character. So those are assumed to have the highest precedence. So we are safe making a pair of parentheses around this one first. The next highest one then would be the plus that we see here. So that gives us here a pair of parentheses. Followed by the equals sign here. So the arrow here would be in parentheses. Of the remaining three, the next highest one would be the caret. Then the bar, and finally the lowest precedence would be the less. So that's the fully parenthesized version of the string I'm after. Now, this might be a fun exercise but you see that, if you are actually asked to read a string like that, then that's maybe less funny. Of course it's perfectly fine to define a mathematical operator such as plus or bar. For something where this operator makes sense. And in some domains it's also perfectly fine to define new symbolic operators that have a special fixed meaning in these domains. But please don't go overboard inventing fancy operator names for all the operations in your API. It's usually not appreciated by the users because they will have a hard time understanding that and also will have a hard time setting the parentheses as you see here.