This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?
e.g. in this example, from scala-arm 1.0:
def toTraversable[B](implicit ev: R <:< TraversableOnce[B]): Traversable[B] =
new ManagedTraversable[B,R] {
val resource = self
override protected def internalForeach[U](resource: R, g : B => U) : Unit =
ev(resource).foreach(g)
}
<:< (and similar strange looking constructs) are defined in Predef.scala (source at scala-lang.org), which is probably the best resource for working out what they are.
In general, classes like that can be used to provide further bounds on the type parameters within the scope of a particular method. <:< in particular is used to require that R is a subtype of TraversableOnce[B].
The description from Predef is:
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.
Related
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.
I am starting to embrace abstract type members over type parameters - mainly because they seem to work better with type inference for me. However, I am still struggling to understand how to use them from outside of the types they are defined in. For example, I cannot understand why the following Scala program should not compile:
trait Thing
trait Describer {
type X<:Thing
def describe(x:X) = println(x)
}
object Program extends App {
def print[T <: Thing, D <: Describer]
(describer: D, thing:T)
(implicit ev: D#X =:= T)
= describer.describe(thing)
}
Intuitively, I would expect that the requirement D#X =:= T would guarantee that the two types are indeed equal and hence that instances of two could be used interchangeably, but I get this compilation error:
error: type mismatch;
found : thing.type (with underlying type T)
required: describer.X
= describer.describe(thing)
What have I misunderstood? Is there another way to do what I want to do? Or failing that, is it safe to cast thing to the required type (describer.X)?
The type of the parameter of describer.describe is actually describer.X and not D#X. Another thing you have to know is that A =:= B also functions as a conversion from A to B but (at least currently) not the other way around. So the following should work.
def print[T <: Thing]
(describer: Describer, thing: T)
(implicit ev: T =:= describer.X)
= describer.describe(thing)
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
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.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?
I'm curious since I saw them in Scala library code, but I found it quite hard to Google something about them since their names are not words.
These classes are used for implicit parameters that restrict the applicability of a method. Below is a description of each class. In general they are useful to restrain the a type parameter of an enclosing class within the context of a single method.
<:<[A,B] or A <:< B
The compiler can provide an implicit instance of this type only when A is a subtype of B. This is similar to A <: B in a type parameter list.
This can be useful when you want to put an additional constraint on a class type parameter in the context of a particular method. For example the class Foo below can be used with any type, but the method bar is only valid when T is a subtype of Number.
class Foo[T](x: T) {
// In general T could be any type
def bar(implicit ev: T <:< Number) = {
// This method can now only be used when T is a subtype of Number
// also, you can use ev to convert a T to number
ev(x).doubleValue
}
}
new Foo(123 : java.lang.Integer).bar // returns 123.0: Double
new Foo("123").bar // compile error: Cannot prove java.lang.String <:< java.lang.Number
=:=[A,B] or A =:= B
The compiler can provide an implicit instance of this type only when A is the same type as B. This doesn't have an equivalent syntax in a type parameter list, you'd just use the same type parameter twice.
This can be used much like <:< except that it requires the types to match exactly. This could be used to make a pair of methods mutually exclusive.
class Foo[T<:Number](x:T) {
def numOnly(implicit ev: T =:= Number) = ()
def floatOnly(implicit ev: T =:= Float) = ()
}
val asFloat = new Foo(123.0f:java.lang.Float)
asFloat.numOnly // Compile error
asFloat.floatOnly // Ok
val asNum = new Foo(123.0f:java.lang.Number)
asFloat.floatOnly // Ok
asFloat.numOnly // Compile error
Essentially if the type parameter is more specific than the constraint you can force the more specific method to be used.
<%<[A,B] or A <%< B
The compiler can provide an implicit instance of this type only when A can be converted to B. This is similar to A <% B in a type parameter list.
This requires that there is an implicit function available to turn an A into a B. This will always be possible when A <: B since the implicit A <:< B satisfies this constraint.
This class is actually marked as deprecated. It says you should instead just use A => B.
<:<, =:= and <%< are generic classes, all of them with two type parameters, and all of them extends Function1 (function with one argument). They are defined in Predef.
They are intended to supply very basic conversion as implicits. The conversions are so basic that most of the time, they are identity. The point of having those classes and not Functions is that they may be distinguished from other functions that might be in implicit scopes. Predef gives the following implicit
For every type A, a <:<[A,A] is available. As <:< is [-From, +To],
<:<[A,A] will satisfy any <:<[A,B] where A conforms to B. The
implementation is identity.
For every type A, there is also a =:=[A,A], but =:= is invariant,
so it will not satisfy any A =:= B if A is not exactly B.
implementation is identity
There is also a A <%< B each time A <% B. Implementation is the
implicit conversion implied by A <% B.
The point is not to provide clever conversions (identity is not too clever), but to provide a library level way enforce some typing constraint at compile time, similar to language level constraint <:, <%, and simple lack of type argument, (which is quite a strong constraint too), in places where the language constraints are not available. A typical example is, in a generic class, when you want a method to be available only for some value of a type parameter. Suppose Collection[A], and I want a method sort that will be available only if A <: Ordered[A]. I don't want to put the constraint in the declaration of the class, because I want to accept collections whose elements are not Ordered. I just don't want them to have a sort method. I cannot put the constraint on the method sort either, because A does not even appear as a type parameter of method sort. <:< provides a solution :
class MyCollection[A] {
def sort(implicit ev: A <:< Ordered[A])
// other methods, without constraints
}
Doing that, sort is technically available on all instances of MyCollection, but practically, when one does not pass ev parameter explicitely, it will look for an A <:< Ordered[A] in implicit scope, and conforms in predef will give one only if A is a subtype of Ordered[A]. As the (identity) conversion between A and Ordered[A] is made available in the implicit scope of the routine, A will be usable as an Ordered[A] in the body of method sort.
Using =:= would force the type to be exactly Ordered[A] (the equivalent constraint on MyCollection would be simply to make it non generic, and put the given type everywhere there is the generic parameter). <%< would ensure there is an implicit conversion. That would be the more likely one here.
Actually for this particular method sort, the proper solution would be to get an Ordering[A] in implicit context, with def sort(implicit ev: Ordering[A]) but that would not have demonstrated the point of those classes.
They are generalized type constraints for type arguments. Check out their definitions along with corresponding implicit conforms() methods in Predef.
In brief it works like this:
you add implicit parameter to your method like implicit tc: T <:< U, that works like implicit tc: <:<[T, U]
there is implicit conforms() method that returns required instance ensures that T <: U (just like with regular generics)
if T <: U is not the case, compilation of method call fails with "implicit not found"
Look at usage sample here: http://java.dzone.com/articles/using-generalized-type