Understanding implicit <:< parameter - scala

I wrote this example
class TestMatch[T](private val t: T){
def test()(implicit ev: T <:< Option[Int]) = println(ev(t).get)
}
and a test for it
val tm = TestMatch(Some(10))
tm.test() //fine
val tm2 = TestMatch(10)
tm2.test() //compilation error
The question is who creates the implicit ev: T <:< Option[Int] when I invoke test method? I know I didn't. Maybe the compiler is aware of implicit <:< and know what to do with it.
Documenation of <:<was not quite clear
To constrain any abstract type T that's in scope in a method's
argument list (not just the method's own type parameters) simply add
an implicit argument of type T <:< U, where U is the required
upper bound; or for lower-bounds, use: L <:< T, where L is the
required lower bound.
Does it mean that the compiler will take the rest on itself? I just add the implicit ev: T1 <:< T2?

The first snippet compiles because of Predef.identity, which means you can always implicitly convert type T to type T (in this case Option[Int]). Otherwise, you would need to bring an implicit into scopre by yourself.

Does it mean that the compiler will take the rest on itself?
The compiler will search for an implicit in scope. If it finds a match, it will provide it, if it can't, you'll get a compilation error. With your example, the compiler finds that Some[Int] adheres to the implicit requirement of Some[Int] <:< Option[Int] as it is a direct subtype of Option[Int].
You can see this when compiling the code with scalac:
val tm: TestMatch[Some[Int]] = new TestMatch[Some[Int]](scala.Some.apply[Int](10));
tm.test()(scala.this.Predef.$conforms[Some[Int]]);
Where's for Int (your second example), there is no implicit in scope matching the requirement, and Int is not a subtype of Option[Int].

The compiler will try to look for implicit parameters in various predefined places. If it cant find them it will throw an error. This link might help: http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

Related

Type parameter under self-type doesn't conform to upper bound despite evidence

I have a trait with a self-type annotation that has a type parameter. This trait is from a library and cannot be modified. I want to pass this trait to a function that will require an upper bound for the type parameter. For example, I have this code snippet:
sealed trait Job[K] { self =>
type T
}
case class Encoder[T <: Product]()
def encoder(job: Job[_])(implicit ev: job.T <:< Product): Encoder[job.T] =
new Encoder[job.T]()
This returns an error that Type argument job.T does not conform to upper bound Product and a warning that ev is never used. How should I design the encoder function?
Why it doesn't work?
Your issue has nothing to do with the generalized type constraint. You can remove it and still get the same error. A generalized type constraint is used to constrain the type of arguments the method can receive.
(implicit ev: job.T <:< Product) provides an evidence in scope that matches only if job.T <: Product, allowing only calls to the method with Job arguments where job.T <: Product. This is its purpose.
Your issue is because the Encoder class has its type parameter T <: Product. The generalized type constraint does not treat the type job.T itself as a subtype of Product, as you expected. The evidence only applies to value arguments, not to the type itself, because this is how implicit conversions work.
For example, assuming a value x of type job.T that can be passed to the method as an argument:
def encoder(job: Job[_])(x: job.T)(implicit ev: job.T <:< Product): Unit = {
val y: Product = x // expands to: ev.apply(x)
val z: Encoder[Product] = new Encoder[job.T] // does not compile
}
The first line compiles because x is expanded to ev.apply(x), but the second one cannot be expanded, regardless if Encoder is covariant or not.
First workaround
One workaround you can do is this:
def encoder[U <: Product](job: Job[_])(implicit ev: job.T <:< Product): Encoder[U] =
new Encoder[U]()
The problem with this is that while both type parameters U and T are subtypes of Product, this definition does not says much about the relation between them, and the compiler (and even Intellij) will not infer the correct resulting type, unless you specify it explicitly. For example:
val myjob = new Job[Int] {
type T = (Int, Int)
}
val myencoder: Encoder[Nothing] = encoder(myjob) // infers type Nothing
val myencoder2: Encoder[(Int, Int)] = encoder[(Int, Int)](myjob) // fix
But why use job.T <:< Product if we already have U <: Product. We can instead use the =:= evidence to make sure their types are equal.
def encoder[U <: Product](job: Job[_])(implicit ev: job.T =:= U): Encoder[U] =
new Encoder[U]()
Now the resulting type will be correctly inferred.
Second workaround
A shorter workaround is using a structural type instead:
def encoder(job: Job[_] { type T <: Product }): Encoder[job.T] =
new Encoder[job.T]()
Which is not only cleaner (doesn't require a generalized type constraint), but also avoids the earlier problem.
Both versions work on Scala 2.13.8.
Expanding on Alin's answer, you may also use a type alias to express the same thing like this:
type JobProduct[K, P <: Product] = Job[K] { type T = P }
// Here I personally prefer to use a type parameter rather than an existential
// since I have had troubles with those, but if you don't find issues you may just use
// JobProdut[_, P] instead and remove the K type parameter.
def encoder[K, P <: Product](job: JobProduct[K, P]): Encoder[P] =
new Encoder[P]()
This approach may be more readable to newcomers and allows reuse; however, is essentially the same as what Alin did.

Calling method with an implicit parameter

I have the following method:
def test[T](implicit ev: T <:< Int, t : T) = println(t)
How can I call it? I tried
test(10)
But the compiler prints out the following error:
Error:(19, 9) not enough arguments for method test: (implicit ev: <:<[T,Int], implicit t: T)Unit.
Unspecified value parameter t.
test(10)
^
First of all, I thought that we could just omit implicit parameters and specify only explicit ones. And secondly, why does it's saying that that the parameter t is implicit?
implicit t: T
How does it actually work?
First of all, I thought that we could just omit implicit parameters and specify only explicit ones.
You either specify all the implicits in the list, or you don't specify them at all. According to the specification, if one parameter is marked as implicit, the entire argument list is marked as well:
An implicit parameter list (implicit p1, ……, pn) of a method marks the parameters p1, …, pn as implicit.
secondly, why does it's saying that that the parameter t is implicit?
Because of what was answered in your first part.
If you still want to invoke it like that, you can use implicitly:
test(implicitly, 10)
Generally, it is recommended that you require an implicit in a separate argument list:
def test[T](i: Int)(implicit ev: T <:< Int) = println(t)
the problem is that the implicit parameter should be in its own list, like this:
def test[T](t : T)(implicit ev: T <:< Int) = println(t)
Give that a try!

How does the Scala compiler synthesize implicit evidence with `<:<`?

Given this (admittedly contrived) code fragment in Scala:
object Main extends App {
class X { def foo = 1 }
def f[A](value: A)(implicit ev: A <:< X) = { value.foo }
println(f(new X()))
}
What does the Scala compiler do to make this pass? I have looked at some code in Predef but I don't understand the implementation. Please give a detailed step by step explanation.
Callsite
Let's look at what the type inferencer does when you write:
f(new X())
It first has to figure out, what the template parameter A of f is. Type inference in Scala goes left to right in argument lists, so trivially (given new X is of type X), we get
f[X](new X)
Now the compiler needs to find an implicit value of type X <:< X (remember, A got resolved to X).
To find implicit values, the compiler looks in various places, amongst others your current scope (in which Predef._ is imported).
The compiler then finds Predef.$conforms:
implicit def $conforms[A]: A <:< A = // some implementation
So this can be used to produce a X <:< X, by invoking it with X as parameter:
f[X](new X)(Predef.$conforms[X])
The actual implementation of $conforms does not matter as far as the type checker is concerned.
Method Implementation
Now lets look at the implementation:
def f[A](value: A)(implicit ev: A <:< X) = { value.foo }
Value is of type A (so something unknown). You want to call foo on value. Since foo is not defined on A, the compiler is looking for an implicit function (or method) that converts A into something that has a foo.
There is such a thing in scope: ev (A <:< B extends A => B).
Therefore, the compiler inserts an implicit conversion using ev:
ev(value).foo
Small Note About Variance
As you might have noticed, <:< is variant in its parameters: <:<[-From, +To]. This can be used to generate actual subtyping evidences. Consider:
class A
class B extends A
val ev1: A <:< A = conforms
val ev2: B <:< A = ev1 // makes sense, works because of variance
// Also
val ev3: B <:< B = conforms
val ev4: B <:< A = ev3 // makes sense, works because of variance
This is notably the reason, why there is no need for a conforms method with two type parameters. Further, note that this behavior is specifically not wanted for =:= (since this is type equivalence), so it is invariant.

How does the <:< operator work in Scala?

In Scala there's a class <:< that witnesses a type constraint. From Predef.scala:
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
An example of how it's used is in the toMap method of TraversableOnce:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] =
What I don't understand is how this works. I understand that A <:< B is syntactically equivalent to the type <:<[A, B]. But I don't get how the compiler can find an implicit of that type if and only if A <: B. I assume that the asInstanceOf call in the definition of $conforms is making this possible somehow, but how? Also, is it significant that a singleton instance of an abstract class is used, instead of just using an object?
Suppose we've got the following simple type hierarchy:
trait Foo
trait Bar extends Foo
We can ask for proof that Bar extends Foo:
val ev = implicitly[Bar <:< Foo]
If we run this in a console with -Xprint:typer, we'll see the following:
private[this] val ev: <:<[Bar,Foo] =
scala.this.Predef.implicitly[<:<[Bar,Foo]](scala.this.Predef.$conforms[Bar]);
So the compiler has picked $conforms[Bar] as the implicit value we've asked for. Of course this value has type Bar <:< Bar, but because <:< is covariant in its second type parameter, this is a subtype of Bar <:< Foo, so it fits the bill.
(There's some magic involved here in the fact that the Scala compiler knows how to find subtypes of the type it's looking for, but it's a fairly generic mechanism and isn't too surprising in its behavior.)
Now suppose we ask for proof that Bar extends String:
val ev = implicitly[Bar <:< String]
If you turn on -Xlog-implicits, you'll see this:
<console>:9: $conforms is not a valid implicit value for <:<[Bar,String] because:
hasMatchingSymbol reported error: type mismatch;
found : <:<[Bar,Bar]
required: <:<[Bar,String]
val ev = implicitly[Bar <:< String]
^
<console>:9: error: Cannot prove that Bar <:< String.
val ev = implicitly[Bar <:< String]
^
The compiler tries the Bar <:< Bar again, but since Bar isn't a String, this isn't a subtype of Bar <:< String, so it's not what we need. But $conforms is the only place the compiler can get <:< instances (unless we've defined our own, which would be dangerous), so it quite properly refuses to compile this nonsense.
To address your second question: the <:<[-From, +To] class is necessary because we need the type parameters for this type class to be useful. The singleton Any <:< Any value could just as well be defined as an object—the decision to use a val and an anonymous class is arguably a little simpler, but it's an implementation detail that you shouldn't ever need to worry about.

Why won't Scala use implicit conversion here?

I'm trying to call this set method documented here, in the Java library jOOQ, with signature:
<T> ... set(Field<T> field, T value)
This Scala line is a problem:
.set(table.MODIFIED_BY, userId)
MODIFIED_BY is a Field<Integer> representing the table column. userId is Int. Predef has an implicit conversion from Int to Integer, so why doesn't it use it? I get this:
type mismatch; found: org.jooq.TableField[gen.tables.records.DocRecord,Integer]
required: org.jooq.Field[Any]
Note: Integer <: Any
(and org.jooq.TableField[gen.tables.records.DocRecord,Integer] <:
org.jooq.Field[Integer]), but Java-defined trait Field is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Update - About Vinicius's Example
Rather than try to explain this in comments, here is a demonstration that there is no implicit conversion being called when you use a type with covariant parameter, like List[+T]. Let's say I put this code in a file, compile, and run it...
case class Foo(str: String)
object StackOver1 extends App {
implicit def str2Foo(s: String): Foo = {
println("In str2Foo.")
new Foo(s)
}
def test[T](xs: List[T], x: T): List[T] = {
println("test " + x.getClass)
xs
}
val foo1 = new Foo("foo1")
test(List(foo1), "abc")
}
You'll see that it calls test, but never the implicit conversion from String "abc" to Foo. Instead it's picking a T for test[T] that is a common base class between String and Foo. When you use Int and Integer it picks Any, but it's confusing because the runtime representation of the Int in the list is Integer. So it looks like it used the implicit conversion, but it didn't. You can verify by opening a Scala prompt...
scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2)
List[Any]
I don't know anything aboutjOOQ, but I think the issue is that Scala does not understand java generics very well. Try:
scala> def test[T](a : java.util.ArrayList[T], b: T) = { println(a,b) }
scala> val a = new java.util.ArrayList[Integer]()
scala> val b = 12
scala> test(a,b)
<console>:11: error: type mismatch;
found : java.util.ArrayList[Integer]
required: java.util.ArrayList[Any]
Note: Integer <: Any, but Java-defined class ArrayList is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
test(a,b)
Sounds familiar??
And to fix, just inform the type T to call the method: test[Integer](a,b) works fine.
EDIT:
There a few things involved here:
Erasure -> When compiled the type of the generic will disappear by erasure. The compiler will use Object which Scala, will treat as Any. However a ArrayList[Integer] is not an ArrayList[Any], even though Integer is any. The same way that TableField[gen.tables.records.DocRecord,Integer] is not a Field[Any].
Type inference mechanism -> it will figure out what type T should be and to do that it will use the intersection dominator of the types passed (in our case the first common ancestor). Page 36 of Scala Language Spec, which in our examples above will lead use to Any.
Implicit conversion -> it is the last step and would be called if there was some type to be converted to another one, but since the type of the arguments were determined to be the first common ancestor, there is no need to convert and we will never have a implicit conversion if we don't force the type T.
A example to show how the common ancestor is used to determine T:
scala> def test[T](a: T, b: T): T = a
scala> class Foo
scala> class Boo extends Foo
scala> test(new Boo,new Foo)
res2: Foo = Boo#139c2a6
scala> test(new Boo,new Boo)
res3: Boo = Boo#141c803
scala> class Coo extends Foo
scala> test(new Boo,new Coo)
res4: Foo = Boo#aafc83
scala> test(new Boo,"qsasad")
res5: Object = Boo#16989d8
Summing up, the implicit method does not get called, because the type inference mechanism, determines the types before getting the argument and since it uses the common ancestor, there is no need for a implicit conversion.
Your code produces an error due to erasure mechanism which disappear with the type information that would be important to determine the correct type of the argument.
#RobN, thanks for questioning my answer, I learned a lot with the process.