In this session, we're going to talk about contextual abstraction, what it is, and how it is supported in Scala. If we look at the word context, it's formed from con, which means Latin with, and text. It means what comes with the text but is not in the text. It's a very general concept which in fact comes in many forms. Contexts could mean the current configuration, which is the same everywhere except maybe at some point you want to change it in some parts of a code, or the current scope, or the meaning of an operation like less than on this type, or going out further, the user on behalf of which an operation is performed, or the security level in effect. It's really very general, it could mean many things. Code becomes more modular if it can abstract over contexts. Abstracting over context means that functions and classes can be written without knowing in detail the context in which they will be called or instantiated. How can we represent contexts in programming? There have been so far several techniques to do that, and they all have downsides. What we can see is we can represent contexts, that's just global values accessible to the program. But that means we have no abstraction and that's often too rigid. That means that global value is just known for everybody and we can't change it anywhere. We might change that to global mutable variables that can be set according to the needs of various program parts. However, what if different modules need different settings that could give you interference where you think you have one setting, but in fact, somebody has changed the variable and you have a different one? Such interference can of course be very dangerous. Some dynamically typed object-oriented languages have introduced a concept called monkey patching. What that means is something similar to global variables, but instead of changing a global variable, you change a property of a root class. For instance, you might have the base class object and you add a new method to object that's visible for everyone, or you change the behavior of a method. That has a slightly more limited scope than globals because it affects only classes inheriting that root class, but it's nevertheless, essentially just a more powerful way to shoot yourself in the foot, I think. Another solution is provided by so-called dependency injection frameworks. Examples are Spring or Guice in the Java world. These have sprung up because programming languages haven't offered out-of-the-box solutions to the problem of how to abstract over contexts. What they do is they're outside the language and essentially rely on bytecode rewriting to add dependencies to programs. That's powerful, but it's also makes programs harder to understand, because now you have to understand the programming language and the bytecode rewriting framework, and it's in particular harder to debug because there's now a lot of code in your way that you haven't written yourself, but some framework has written. All this was very imperative. What would be a functional way to represent a context? Well, in functional programming, the natural way to abstract over context is with function parameters. If you want to abstract over something that is known from the outside of a function, then make it a function parameter. That's, of course, very flexible. It's also type-safe, since parameters have types and the types are checked, your programs become safer, you can't have a type error when you read something from the context, and it's not relying on side effects, which is definitely a bonus. Why is not everybody doing that? Well, sometimes this is too much of a good thing. If you use that technique extensively, it can lead to many function arguments, and most of them are the same from one code to the next. They hardly ever change, which of course means your code will be very repetitive and in all these repetitions, errors will be hard to spot. That's a downside of essentially overdoing this functional parameter approach to context representation. But as we will see, it has a solution in Scala. Let's demonstrate the problem with an example. You've already seen sort functions. For instance, here's the outline of a method sort that takes as a parameter a list of ints and returns a list of int. Somewhere in the body of sort, you would find a comparison of two list elements, so if x less than y, something like that. Now that worked for list of ints, but of course we want a more general sort that works for lists of arbitrary types. Here it is, that's sort function which now takes a type parameter. But this does not work because there's not a single comparison method less than that works for all types T. If you would write less than here, you would get a type error which says that the type parameter T doesn't have a less than method. In other words, we need to ask the question, what is the meaning of less than on type T? The actual instance of type T at the call site. This means we need to query the call site contexts. Let's apply our strategy to represent contexts as parameters, that's the most flexible design. We just passed the comparison operation as an additional parameter. Sort would now take a list and a less than method which compares two element of type T and instead of having the less method directly, we write less than x, y. If that works, we can now call sort as follows. Let's say we have a list of ints and the list of strings like that. Then we would write sort ints with the less than method which is integer comparison here, and sort strengths with the less than method, which is a string compared to less than zero here. In each case, we have passed the appropriate method to compare the elements of the list. In fact, there's already a class in the standard library that represents orderings with less than methods and also less than or equal, greater, and so on. It's called scala.math.Ordering, and it's parameterized by the type for which the ordering is defined. That method provides ways to compare elements of type A, which means that instead of parameterizing with the less-than function, we could parameterize with ordering instead, so that's what this would look like. We would have the sort method, and it takes an ordering of T, and then instead of less than, we would write ord.lt which is essentially the less than method defined in that ordering. Calling the new sort method would look like this, we would import scala.math.Ordering, and then we sort the integers list with the integer ordering, ordering.Int and the strings list with ordering.String. That makes use of the values int and a string in the ordering object which define the orderings for the types int and string respectively. For instance, here would be the int value in the ordering object, so it's an ordering for int, and what we have to do is we have to define a compare method that will give us back minus 1 if x is less than y plus 1 if x is greater than y and 0 if they're equal. It would have the typical compare method for integers. Then the LT and the other less than, greater than comparison methods would all be defined in the class ordering in terms of compare. Of course, the ordering.String would be defined analogously. This works, but there's a problem. Namely, that passing around these ordering arguments is quite cumbersome. Sort ints ordering of int sort strings, ordering of strings looks heavy. You could argue that sorting a list of int value should always use the same ordering int as an argument where list of string value should always use ordering of strings, so why make it mandatory to add these things. Can't the compiler figure that out? In fact, we can let the compiler in further can reduce the boilerplate by making ord an implicit parameter. Make it an implicit parameter just by prefixing it with the keyboard using as you see here. Now, ord is an implicit parameter which means that the compiler can synthesize the correct arguments that match the ord parameter. You can omit the parameter and just write sort of ints and the compiler will figure out it needs an ordering.int argument here, or you can write sort of strings and the compiler will supply an ordering.String. Generally, the compiler infers the argument value based on its expected type and of course here you know, the compiler knows at this point that since you sort a list of ints, you need an ordering of type int and here it needs an ordering of type string. The expected type is known, and with that expected type, you tell the compiler to figure out what the right argument value here is. What happens there is, in a sense the deal of type inference. Type inference is when the compiler infers types from values. For instance, in the previous calls to sort that we see here what the compiler really does. It supplies the type parameter int in the first case and string and the second case. That it infers from the type of the actual values that get passed to sort. But the Scala compiler is also able to do the opposite, namely to infer expressions which we sometimes called terms from types and so consequently this is called term inference. When there's exactly one obvious value for a type in a using clause, the compiler can provide that value to us. It will further go and say, well, sought int, ints will actually be augmented with the argument ordering.int and sort string, strings will be augmented with the argument ordering.String. This looks very useful and at the moment it also looks a little bit like magic, so in the following sessions, we will explain the precise rules for this to happen.