So far, all our programming was about functions that we're operating on values of primitive data types, such as into a string. This will change from now, we'll introduce ways to introduce new customized data structures. We'll do that with an example, namely rational numbers. We want to design a package for doing rational arithmetic. A rational number x over y is represented by two integers, its numerator x and its denominator y. Suppose we want to implement addition of rational numbers. Without having a special type for Rational numbers, we could still write something like this. Add Rational numerator, gets the Rational numbers in pairs of two integers here and here. Numerators here, denominators here, and would give you according to the formulas, the numerator of the new Rational number. Then you'd need another complimentary function, add Rational denominator that would give you the denominator. But of course it would be really difficult to manage all these numerators and denominators separately. A better choice is to combine the numerator and denominator of a Rational number in one data structure. In Scala, we do this by defining a class. Here you see a possible start of class Rational. You see two parameters here, which are the parameters that become the numerator and the denominator. Numerator and denominator are methods, so-called, methods inside class Rational. A class definition like Rational introduces two entities. It introduces a new type named Rational. In the future, one can use Rational just like one can use int or string. It also introduces a constructor, also named Rational to create the elements of this type. There's no confusion because Scala keeps the names of types and values in different namespaces. Otherwise put, it's always clear from the context whether we talk about a type or about a value, and constructors form part of the values in reverse. We call the elements of a class type, objects. We create an object by calling the constructor of the class. Rational (1,2) would create the Rational one over two. Once we have a Rational number, we can select its members using the infix operator dot. If x is the Rational one over two, then x.numer would give you one and x.denom, which would give you two. We can now define the arithmetic functions that implement the standard rules. Here you see the formulas for the addition of two Rational numbers, subtraction, multiplication, division, and equality. One way to do that would be to add new functions outside class Rational. For instance, the add Rational function would take two Rationals, r and s, and implement the standard formula for addition. It takes the numerator of the left, multiplies with the denominator of the right, then the numerator of the right gets multiplied with a denominator of the left. The denominator of the whole thing is the product of the denominators of the two Rationals. Another thing we might want to add is a method, makeString which takes a Rational number and turns it into a string. Our Rational number 1.5 should be printed as one slash two. The way we do that is we use here what's called an interpolated string. That syntax s quote mark to quote, has a special meaning in Scala, it means that inside these interpolated strings, anything that follows a dollar is treated as an expression, so it's evaluated and then the string value of its result is inserted into that string. What this expression here would do is, it would take the numerator, that's one, convert it to the digit one. Here it would take the denominator convert it to the digit two, and then splice these results into the resulting string with the slash in between. That's what we will get. Once we have next string, we can write expressions like this one here. We can add two Rational numbers, one, two, and two,three, and convert the result to a string. We would expect to see seven slash six. One can go further into also package functions that operate on a data abstraction in the datatype itself. Such functions that are inside a class are called methods. Rational numbers now could have, in addition to the functions numer and denom, the functions add, subtract, multiply, divide, equals, and toString. Here's a possible implementation. We have the class rational that starts as before. Then we could add a method add that takes a rational number as argument. If you have a rational number x and one which is y, then you would invoke it like this, x.add of y. It's like.numer, but you add an argument here. That means that what the argument here is the right operand of the addition operation and the left operand is the rational number itself in which the add occurs. Consequently, the formula to create a new rational number, it's almost the same as our previous stand-alone method, add rational, but instead of writing the left-hand side.numerator, you just refer to numerator directly. It's that numerator of the current rational, and we multiply it with the denominator of the other rational number, and analogously, for the other parts of the formula. That was addition. In the same vein, I can define multiplication. What about MakeString? In fact, there is already a standard method toString that all Scala classes support and that you can customize as you want. The idea is that for rational, we would customize this toString method to just return the interpolated string. It's numer denom. We could put it in braces as before, but if the expression is a simple identifier like numer denom, we don't have to. We can write it shorter, just $numer divided by $denom. One thing to note is that we had used the modifier override in front of the def. That's because, like I said, every class already defines a toString method. The override modifier essentially states that we do not want this default definition of toString, but we want to use our own in-class rational. Here's how one might use the new rational abstraction. We can define three rational numbers here, x, y, z, and then add x and y, and then multiply the result with z. In each case, the method gets called with the infix dot and the argument of the method is just passed as you would pass a function argument. Let's put this to the test. I have defined my class rational, I've added a multiplication method, and now let's just define three numbers and add them and multiply them. What we see is not very comprehensible. What we got here is actually the toString that the class rational got by default with so-called inherited from class java.lang.Object. That one is not very nice because, basically, it just tells you what the type is and then it tells you a hash code afterwards. Not something for human consumption. What we need to do is we need to add the toString method as indicated. We say def toString equals interpolated string of numer and then denom. Then we get an error that says we can't override this method toString in class Any because we need an override modifier. The compiler helpfully tells us that we are changing the default behavior and we have to be explicit about it. Once we do that, then we get still not quite the right thing because we forgot to put a dollar here. Let's do that. Now everything looks nice and tidy. Let's finish this session with an exercise. In your worksheet, add a method neg to class rational that's used like this, x.neg should evaluate to minus x. Once you've done that, add a method sub to subtract two rational numbers. As a test, you might want, with the numbers x, y, z as given in the previous slide, try out the expression x minus y minus z. Let's get to that. How would we define the neg operation? Well, a simple way to do it is to just create a rational with a negated numerator and the denominator. Now, for subtraction, we'd subtract the rational from the current object, but we could alternatively write an expression just like this one here except with a minus, or maybe more elegantly, we just make use of neg. It would be add r.neg. We add the negated rational and that gives us subtraction. Now let's do the x subtracted by y subtracted by z. That gives us the result that you see here.