In this session, we'll explain the magic I alluded to in the last session where the compiler could infer a value for our type. It comes down to using clauses and given instances. An implicit parameter is introduced by a using perimeter clause, that's the one you have seen "using ord: Ordering of T." If you have a parameter clause like that, we can pass a matching explicit argument in a using argument clause. You repeat using again, you write, sort strings using ordering of string and that would match up with this parameter. But the point is that arguments for implicit parameters can also be left out, and they usually are. If the argument is missing, the compiler will infer one from the parameter type. Sort of strings will become sort of strings using ordering of string. Here are some variations of using classes. You can have multiple parameters in a using clause, but then you write the keyword using only once at the front of them all. Analogously, you can also have multiple arguments with a single using in the front, or you can have several using clauses in a row. For instance, you could have a using clause for A here followed by a using clause for B or you can mix using clauses freely with regular parameters. Here you would have a case f where you have irregular parameter x using clause, a, regular parameter, y and using clause b. If you pass arguments explicitly, then they follow exactly that pattern x using a, y using b. But this one here, or this one here can also be left out. You might ask if I have two using clauses that follow each other and then I write a single using argument clause, like as in this, using y, which one will be matched? The answer is it's the first one. Essentially you fill in using clauses from the left. If there are no using argument clauses left then the rest will be inferred by the compiler. Parameters of a using clause can also be anonymous. You could write just using ordering of T and leave out the ord parameter. That's useful if the body of sort does not mention ord at all but simply passes it on as an implicit argument to further methods. You see that situation here where the only use of the ord parameter is to pass it on to the merge method that does the actual comparisons. Code that you see here is analogous to the following code where the parameter names and put back and the arguments are made explicit. Here everything is named and I pass ord explicitly. Here the parameters are anonymous and the implicit argument is inferred. This pattern where you have a type parameter T and then a using clause that uses T in some trait like ordering is actually quite common. This syntax colon ordering is called a context-bound in analogy to add to an upper bound or a lower bound, which you know already. So instead of having, let's say, an upper bound for T. We have essentially a constraint that says there must be an instance for ordering that is defined on T. That's another form of bound which is called context bound because it refers to the situation at the call site of the print sorted method in this case. More generally, a method definition such as this one here, where T is followed by a number of types, each following a colon would be expanded to just a simple type parameter T. Then for each of these types here, you have a using parameter, an implicit parameter U_1 of T up to U_n of T. So the T gets repeated each time in the argument of these type constructors U_1 to U_n. We've seen previously that a parameter in a using clause, such as using ordering Int gets instantiated with an ordering of Int value. For this to work, that ordering of Int must be defined as a given instance. You see here the given instance for that value Int, it's written: given Int, its name, then the type it implements, that's ordering of Int and then the methods that need to be implemented for an instance of that type. In our case ordering was a trait that had a compare method that gets implemented here in the given instance. That code defines a given instance of type ordering of Int named Int. Given instances can also be anonymous. You can just omit the instance name, an example you see here. To define an anonymous instance of type ordering of double, you just write given ordering of double and then the definition of compare for that type. You can leave out the name for the given instance. In that case, the compiler will synthesize a name. Here you see it, it's the name would read here given_Ordering_Double, and the rest would be as the same as what you have defined. Anonymous givens are nice because they're concise and concentrate on what's most important. Namely, what type do you implement? The name usually doesn't matter to the same degree. Sometimes you might find that two given instances give you a name clash because the generated name is the same. Essentially the two types were too close to each other. They lead to the same generated name. In that case, basically, you just fall back to named given instances to avoid the clash. We can summon an instance, which means we can refer to the instance, whether it's named or anonymous, by its type. That's done with the summon method. So summon followed by a type will create the value, the instance, that is the given instance for that type. Summon ordering of Int would expand to Ordering of Int, that's the given instance. Or summon Ordering of Double would expand to Ordering, and then the name that the compiler has generated for this Double Ordering. Summon is a regular method. It's in the predef object which is imported automatically into every Scala code. But it could also be defined like this: you could define summon, takes a type parameter, T, and an instance of type T with a using clause, and it simply returns the instance. That's all there is to summon. Now for the fine print. Say you have a function that takes an implicit parameter of type T, and the function call doesn't have a corresponding argument. In that case, the compiler will search a given instance that has a type which is compatible with T and that is visible at the point of the function call, or it is defined in a companion object associated with T. We'll get to what that means in detail in the next slides. If there is a single such instance or a most specific instance, we'll get to that as well, then that instance will be taken as the actual argument for the inferred parameter. If there's not such an instance, it will be a type error. What we still have to clarify is, where exactly is the compiler looking for given instances and how does it decide whether an instance is more specific than another? These two points need to be clarified now. Where does the compiler look for a given instance? The search for an instance includes: all the instances that are visible at the point of call. Visible means they could be defined in an enclosing scope, or they could be inherited by an enclosing class, or they could be imported. If it doesn't find a given instance in the enclosing scope, it will also look at given instances in the companion objects that are associated for the query type T. This definition of when a companion object is associated is actually quite general. It means that if T is a class, and the class has a companion object here, then a given instance in that companion object will be found because the companion object is associated with the class type C. Beyond that companion object of the class itself, the compiler will also look in the companion objects that are associated in any of T's inherited types. So if class T extends, let's say a base class, B, then it will also look in the companion object of B. That's another candidate here. Or the companion object might be associated with any type argument of T. So it's not just the type as a whole, but any part of a type that can give rise to companion objects that are searched for given instances. Finally, if T is an inner class, the outer objects in which it is embedded also contribute to the component objects associated with T. So if we look at, let's say we have an object, O, and a Class, C, and we have a given instance here, and we need a given instance for the type O.C, then that T here would be eligible because it's not a companion object of C, but it's defined in an outer object. In summary, basically, every object that can be reasonably connected with the type T will be searched, and that means that you don't need to worry too much where you put your given instances, as long as they have some connection with the type, then it will be an associated companion and the given instance will be found. The search for given instances is done in two phases. It will first look in the lexically enclosing scope for something that's visible, and only if that fails, it will look for companion objects associated with T. Here's an example. We have the class hierarchy that you see here, and we want to know, what are the companion objects associated with the type Bar of Y? Which companion objects would be searched for given instances? In fact, in this case, the companion objects would be the companion object of Bar, because that's the class that you have here; the companion object of Y, because that's a parameter of Bar; companion object of X, because that's a base type of Y; and the companion object of Foo, because that's a base type of Bar. But Baz of T doesn't count because it's not a companion object of a base type of either Bar or Y. I said one way to make a given instance visible is to import it. But that raises a question; given instances can be anonymous, so how can we import given instances that don't have a name? In fact, there are three ways to import a given instance: The first way is by-name. That's just the same as importing any other definition by name. For instance, scala.math.Ordering.Int could import the given instance with the name Int from the scala.math.Ordering object. The second form is new and it applies only to given instances. We can also import them by type. For instance, here you see we import scala.math.Ordering, and then we write given Ordering of Int in braces. What that does is that it will import the given instance in that object that implements Ordering a type Int. So that is a given instance of that type. We can also be more general instead of having a particular argument like Int, we can also have a question mark which is a wildcard. So here the second input would import any given instance that implements an ordering object of an arbitrary type. So it would import the ordering of Int and the ordering of Boolean and the ordering of double and any other orderings that were in the Scala math.ordering object. A third way to import a given instance is as a wildcard, but the wildcard is different. Instead of using an underscore for a wildcard for normal values, we write given, and that makes it explicit that we in fact want the given instances and only the given instances from the Scala math objects, but not the nominal values. So which form of import should you use normally? What I would say, since the names of given don't really matter, use the second form. It's the most informative. It tells you exactly what you want to know, namely that we get the given instances for a specific type. Here's a little exercise. If you have this program here, so we define the list, we sort it with the sort method called that we've seen before. Where does the compiler in this case find the given instance of type ordering of Int? Was it in the enclosing scope via an import, or in a companion object associated with the type? Well, in this case the ordering Int was defined in a different module, so it wasn't an enclosing scope and we didn't have to import it explicitly. It was found because it was a companion object of the type ordering, given instance is found in that companion object. So if there's no available given instance that matches the query type, you get an error. For instance, here you write def f using n Int, and you just write f, you will get no implicit argument of type Int was found for the parameter n of method f. The other kind of error you might get is an ambiguity that happens if there's more than one given instance that's eligible. Here you see an example. You have a trait C, and you have two given instances of C, c1, and c2, which both define the value x to be different values one or two. We have a function definition f with an implicit parameter of type c. Now if you just call f, then we would get an error which says ambiguous implicit arguments, both value c1 and value c2 match type C of the parameter c of method f. What do you do when you get an error like that? Well, one thing you could do is just pass the argument explicitly. For instance, you could write f using c2 if it was c2 you wanted. You don't always get an ambiguity if there are several given instances that match the same type because it could be that one of them is more specific than the others. In essence, a definition given a of type A is more specific than a definition given b of type B if one of several conditions apply. So if a is in a closer lexical scope than b, so it's nested more deeply than b, then it takes priority. It will be selected instead of b. Or if a is defined in a class or object which is a subclass of the class which defines b, then again a takes priority. Or if these conditions don't apply but the type A is a generic instance of type B, or is a subtype of type B then again, the A instance will be chosen over the B instance. Let's demonstrate this with some examples. What given instance is summoned here? Summon A Int when you have these two things. You have a universal one which takes a type parameter and using argument, and that is an instance of A of T for any T, and you have a specific one that is an instance of just A of Int. So the answer is this specific one will be chosen because it's an instance of the generic type. So the more specific the type is, the more specific the given instance is as well, and more specific instances will be chosen over more general ones. What given instance is summoned here? You have a trait A and a given instance ac, and the trait B and a given instance bc, and then an object O that extends B and we want to C. Well, it will be this one here because it's defined in a subclass. So instances in subclasses are more specific than instances in base classes and parent classes. Finally, what given instance is summoned here? You have an outer instance ac, and then a function f with an instance b inside f, and then a definition that needs a C. So it will be the inner instance that is chosen, this one here. To conclude in this lecture, we've introduced a way to do type-directed programming, so the types direct our programming and even generate parts of our program with the help of a language mechanism that infers values from types. The idea is that there has to be a unique, or at least most specific given instance that matches the queried type for it to be used by the compiler for synthesizing a value of the type. Given instances are searched in the enclosing lexical scope, so imports, parameters, inherited members, as well as normal definitions, and then if that doesn't give a match in the companion objects that are associated with the queried type.