简体   繁体   中英

Implicits via method/constructor arguments vs using implicitly

This code compiles and does exactly what one expects

class MyList[T](list: T)(implicit o: Ordering[T])

However this does not:

class MyList2[T](list: T) {
  val o = implicitly[Ordering[T]]
}

And I can't see why. In the first example when the class is being constructed the compiler will find the Ordering implicit because it will know the concrete type T . But in the second case it should also find the implicit since T will already be a concrete type.

In the first example when the class is being constructed the compiler will find the Ordering implicit because it will know the concrete type T.

In the first example, one needs to have an implicit Ordering[T] in scope for the compiler to find it. The compiler by itself doesn't "make up" implicits. Since you've directly required one to be available via the second parameter list, if such an implicit exists, it will be passed down to the class constructor.

But in the second case it should also find the implicit since T will already be a concrete type.

The fact that T is a concrete type at compile time doesn't help the compiler find an implicit for it. When we say T is a concrete type, you must remember that at the call-site, T is simply a generic type parameter, nothing more. If you don't help the compiler, it can't give the guarantee of having an implicit in scope. You need to have the method supply an implicit, this can be done via a Context Bound :

class MyList2[T: Ordering](list: T)

Which requires the existence, at compile time, of an ordering for type T . Semantically, this is equivalent to your second parameter list.

You must always tell the compiler that an implicit should be provided for your type. That is, you must always put implicit o: Ordering[T] . What implicitly does is that it allows you to access the implicit in case you haven't named it. Note that you can use syntax sugar (called "context bound") for the implicit parameter, in which case implicitly becomes neccessary:

class MyList2[T : Ordering](list: T) {
  val o = implicitly[Ordering[T]]
}

Type [T : Ordering] is a shorthand for "some type T for which an implicit Ordering[T] exists in scope". It's the same as writing:

class MyList2[T](list: T)(implicit o: Ordering[T]) {

}

but in that case implicitly is not needed since you can access your implicit parameter by its identifier o .

But in the second case it should also find the implicit since T will already be a concrete type.

Scala (and Java) generics don't work like C++ templates. The compiler isn't going to see MyList2[Int] elsewhere, generate

class MyList2_Int(list: Int) {
  val o = implicitly[Ordering[Int]]
}

and typecheck that definition. It is MyList2 itself which gets typechecked, with no concrete T .

For the second to work, you need to specify that there is an Ordering for type T using a context bound :

class MyList2[T : Ordering](list: T) {
  val o = implicitly[Ordering[T]]
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM