I'm playing with Scala's type system with the following example:
scala> sealed trait A
defined trait A
scala> case class A1(n: Int) extends A
defined class A1
scala> case class A2(n: Int) extends A
defined class A2
scala> case class MyCase[+P <: A](a: A, y: String)
defined class MyCase
scala> val l = List(MyCase[A1](A1(1), "A1"), MyCase[A2](A2(2), "A2"))
Now, when I do the following:
scala> l.head.asInstanceOf[MyCase[A2]]
res2: MyCase[A2] = MyCase(A1(1),A1)
How can a MyCase[A1] instance be assigned to a MyCase[A2] reference? I mean MyCase[A1] and MyCase[A2] are at the same level in the object hierarchy!
As I understand you're wondering why you didn't get an error here in runtime, when you did l.head.asInstanceOf[MyCase[A2]]. So the answer is simple - type erasure. For JVM both types are equal as it ignores generic parameter - it simply doesn't know about them as they exist on compiler level only, so only compiler can give you an exception:
scala> val mcA2: MyCase[A2] = l.head
<console>:15: error: type mismatch;
found : MyCase[Product with Serializable with A]
required: MyCase[A2]
val mcA2: MyCase[A2] = l.head
^
However, asInstanceOf ignores this check. So when you do
case class MyCase[+P <: A](a: P) //P instead of A
scala> val k = MyCase[A1](A1(0)).asInstanceOf[MyCase[A2]]
k: MyCase[A2] = MyCase(A1(0))
scala> val a: A2 = k.a
java.lang.ClassCastException: A1 cannot be cast to A2
... 33 elided
But in your example - you never really used P in runtime (and it's impossible), so you never gonna get ClassCastException. Actually, under these conditions (no real instances), you can do type-level copy like here, but it safer to use traits for that.
Related
I have below Scala code.
This function defines type parameter type B which is subclass of A. It converts java.util.List[A] into java.util.List[B].
import java.util
import java.util.stream.Collectors
class Animal
class Dog extends Animal
class Cat extends Animal
object ObjectConversions extends App {
import java.util.{List => JList}
implicit def convertLowerBound[ B <: Animal] (a: JList[Animal]): JList[B] = a.stream().map(a => a.asInstanceOf[B]).collect(Collectors.toList())
val a= new util.ArrayList[Animal]()
a.add(new Cat)
convertLowerBound[Cat](a)
}
When I compile this program I get below error.
<console>:15: error: type mismatch;
found : java.util.List[?0]
required: java.util.List[B]
Note: ?0 >: B, but Java-defined trait List is invariant in type E.
You may wish to investigate a wildcard type such as `_ >: B`. (SLS 3.2.10)
implicit def convertLowerBound[ B <: Animal] (a: JList[Animal]): JList[B] = a.stream().map(a => a.asInstanceOf[B]).collect(Collectors.toList())
What is wrong with my program. How can I resolve this error
It looks like type erasure happened (from Animal to ?0) when using java.util.stream.Stream.map, regardless B or concrete type passed in, possibly due to incompatibility between Scala type inference and Java type inference.
When calling Java method and you still want generic type inference, you need to pass the generic type specifically:
def convertLowerBound[ B <: Animal] (a: JList[Animal]): JList[B] = a.stream().map[B](a => a.asInstanceOf[B]).collect(Collectors.toList[B]())
Then you can do your operation successfully:
scala> def convertLowerBound[ B <: Animal] (a: JList[Animal]): JList[B] = a.stream().map[B](a => a.asInstanceOf[B]).collect(Collectors.toList[B]())
convertLowerBound: [B <: Animal](a: java.util.List[Animal])java.util.List[B]
scala> convertLowerBound[Cat](a)
res30: java.util.List[Cat] = [Cat#6325af19, Dog#6ff6743f]
On the other hand, your conversion isn't really useful, because during runtime, all the generic type will be erased so List[A] or List[B] will be the same after compiling (they will become List of Object. You can take a look at the compiled bytecode). You can simply do direct casting on your List instead of on each element:
def convertLowerBound[B <: Animal : TypeTag] (a: JList[Animal]) = a.asInstanceOf[JList[B]]
Then you can do your use case successfully:
scala> a.add(new Cat())
res16: Boolean = true
scala> convertLowerBound[Cat](a)
res17: java.util.List[Cat] = [Cat#6325af19]
However when there is a type mismatch, e.g. you add a Dog in the List and try to cast it as a List of Cat, an error will occur, only when you try to access the element:
scala> a.add(new Dog())
res19: Boolean = true
scala> convertLowerBound[Cat](a)
res20: java.util.List[Cat] = [Cat#6325af19, Dog#6ff6743f]
scala> convertLowerBound[Cat](a).get(1)
java.lang.ClassCastException: Dog cannot be cast to Cat
... 28 elided
I'm trying to describe the types which a case class contains.
import shapeless._
import shapeless.ops.hlist.LiftAll
trait Desc[T] {
def description: String
}
case class Foo(f: Int)
object Foo {
implicit val description: Desc[Foo] = new Desc[Foo] { val description = "foo" }
}
case class SomeCaseClass(f: Foo)
val gen = Generic[SomeCaseClass]
val lifted = implicitly[LiftAll[Desc, gen.Repr]].instances.toList
Gives me
could not find implicit value for parameter toTraversableAux: shapeless.ops.hlist.ToTraversable.Aux[shapeless.ops.hlist.LiftAll[Playground.this.Desc,Playground.this.gen.Repr]#Out,List,Lub]
not enough arguments for method toList: (implicit toTraversableAux: shapeless.ops.hlist.ToTraversable.Aux[shapeless.ops.hlist.LiftAll[Playground.this.Desc,Playground.this.gen.Repr]#Out,List,Lub])toTraversableAux.Out.
Unspecified value parameter toTraversableAux.
Scastie here: https://scastie.scala-lang.org/bXu71pMQQzCqrrsahVBkWA
When you summon an implicit instance with implicitly[LiftAll[Desc, gen.Repr]] then the dependent type Out of LiftAll is lost, so the compiler doesn't know which type exactly instances will return.
To work around this problem most typeclasses in Shapeless define an apply method in their companion object which does retain all dependent type information. It's the reason that you can use gen.Repr in a meaningful way after calling val gen = Generic[SomeCaseClass]. For some reason however LiftAll.apply was not implemented in this way. So that leaves you the option of implementing your own implicitly, or since you're using Shapeless anyway, use its the which is supposed to be a better implicitly.
scala> def impl[T <: AnyRef](implicit ev: T): ev.type = ev
impl: [T <: AnyRef](implicit ev: T)ev.type
scala> impl[LiftAll[Desc, gen.Repr]].instances.toList
res1: List[Desc[Foo]] = List(Foo$$anon$1#40b3708a)
scala> the[LiftAll[Desc, gen.Repr]].instances.toList
res2: List[Desc[Foo]] = List(Foo$$anon$1#40b3708a)
You can see the difference here in the inferred types that the REPL displays:
scala> impl[LiftAll[Desc, gen.Repr]]
res3: LiftAll.Aux[Desc,Foo :: HNil,Desc[Foo] :: HNil] = shapeless.ops.hlist$LiftAll$$anon$206#384d060c
scala> implicitly[LiftAll[Desc, gen.Repr]]
res4: LiftAll[Desc,gen.Repr] = shapeless.ops.hlist$LiftAll$$anon$206#30787774
In the following example, it seems that the Scala compiler only recognizes an implicit class when it is defined to take the higher-kinded representation of Wrapper. Why is that?
scala> case class Nested(n: Int)
defined class Nested
scala> case class Wrapper[A <: Product](nested: A)
defined class Wrapper
scala> implicit class I1[W <: Wrapper[A], A <: Product](underlying: W) {
| def ok1() = true
| }
defined class I1
scala> Wrapper(Nested(5)).ok1()
<console>:26: error: value ok1 is not a member of Wrapper[Nested]
Wrapper(Nested(5)).ok1()
^
scala> implicit class I2[W <: Wrapper[_]](underlying: W) {
| def ok2() = true
| }
defined class I2
scala> Wrapper(Nested(5)).ok2()
res1: Boolean = true
Is there a workaround for implicit resolution that maintains full information about the nested type, allowing typeclass evidence, e.g., TypeTag, to be attached to it?
Note: the example above shows Nested and Wrapper to be case classes but that's not integral to the question. It's simply a convenience for a shorter and simpler console session.
This is happening because of a limitation in Scala's type inference. See SI-2272.
The implicit fails to resolve because the compiler cannot properly infer A. We can see this if we enable -Xlog-implicits. Notice that A is inferred as Nothing:
I1 is not a valid implicit value for Test.w.type => ?{def ok: ?} because:
inferred type arguments [Wrapper[Nested],Nothing] do not conform to method I1's type parameter bounds [W <: Wrapper[A],A <: Product]
The same thing happens if we try to instantiate I1 manually:
scala> val w = Wrapper(Nested(5))
w: Wrapper[Nested] = Wrapper(Nested(5))
scala> new I1(w)
<console>:21: error: inferred type arguments [Wrapper[Nested],Nothing] do not conform to class I1's type parameter bounds [W <: Wrapper[A],A <: Product]
new I1(w)
^
<console>:21: error: type mismatch;
found : Wrapper[Nested]
required: W
new I1(w)
^
Now, the work-arounds.
First, Wrapper is a case class, so there shouldn't be a reason for it to have sub-types. You can remove the W type parameter, and change underlying to a Wrapper[A]:
implicit class I1[A <: Product](underlying: Wrapper[A]) {
def ok = true
}
If you still wish to require two type parameters, you can also require implicit evidence that W <:< Wrapper[A], while removing the upper-bound on the type parameter W:
implicit class I1[W, A <: Product](underlying: W)(implicit ev: W <:< Wrapper[A]) {
def ok = true
}
Everything Michael said is true. Here is some extra perspective on this issue.
Because of the way you wrote your implicit class it looks like you want the implicit class to work on all subtypes of Wrapper and have as specific information about all types involved as possible. (99% of the time it's a bad idea to extend case classes, but it is possible, and these tricks also work for non case classes).
The trick basically is to make sure that all the type parameters that you want inferred are present somewhere in the value parameter lists. Another thing to keep in mind is this:
scala> trait Foo[A]; trait Bar extends Foo[Int]
defined trait Foo
defined trait Bar
scala> implicitly[Bar with Foo[Int] =:= Bar]
res0: =:=[Bar with Foo[Int],Bar] = <function1>
Take these two pieces of knowledge and you can rewrite your implicit class like this:
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
And see it at work:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Nested(n: Int)
case class Wrapper[A <: Product](nested: A)
class Crazy(override val nested: Nested) extends Wrapper[Nested](nested)
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
// Exiting paste mode, now interpreting.
scala> :type Wrapper(Nested(5)).ok1()
(Wrapper[Nested], Nested)
scala> :type new Crazy(Nested(5)).ok1()
(Crazy, Nested)
Note that the last solution Michael gave is based on the same thing: by moving the upper bound to the implicit parameter list, A is now present in the value parameter lists and can be inferred by the compiler.
I have a Scala collection that contains objects of different subtypes.
abstract class Base
class A extends Base
class B extends Base
val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)
I'd like to filter out all the A objects or the B objects. I can do this easily if I know the object I want to filter on at compile time.
s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs
Can I do it if I only know the object type to filter on at runtime? I want to write a function like this.
def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])
Where t indicates whether I want objects of type A or B.
Of course I can't actually write it this way because of type erasure. Is there an idiomatic Scala way to work around this using type tags? I've been reading the Scala type tag documentation and relevant StackOverflow posts, but I can't figure it out.
This has come up a few times. Duplicate, anyone?
scala> trait Base
defined trait Base
scala> case class A(i: Int) extends Base
defined class A
scala> case class B(i: Int) extends Base
defined class B
scala> val vs = List(A(1), B(2), A(3))
vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))
scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]
scala> f[A](vs)
res0: List[A] = List(A(1), A(3))
Type erasure will destroy any information in type parameters, but objects still know what class they belong to. Because of this, we cannot filter on arbitrary types, but we can filter by class or interface/trait. ClassTag is preferable to TypeTag here.
import scala.reflect.ClassTag
def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
case x: T => x
}
Which we can use like:
scala> filterType[B](s)
res29: List[B] = List(B#42096939)
scala> filterType[Base](s)
res30: List[Base] = List(A#8dbc09c, A#625f8cc7, B#42096939)
This method is safe at run-time if type T is not generic. If there was a class C[T] extends Base we could not safely filter on C[String].
Given classes Parent and Child.
scala> class Parent
defined class Parent
scala> class Child extends Parent
defined class Child
Define implicits for Parent and Child
scala> implicit val a = new Parent
a: Parent = Parent#5902f207
scala> implicit val b = new Child
b: Child = Child#3f7d8bac
Use implicitly to find out which implicit gets resolved.
scala> implicitly[Child]
res1: Child = Child#3f7d8bac
illustration of my understanding:
Parent
|
Child -- implicit resolution gets the most specific, lowest sub-type
Now, let's use a contravariant type.
scala> trait A[-T]
defined trait A
scala> case class Concrete[T]() extends A[T]
defined class Concrete
Then define a Parent and Child class.
scala> class Parent
defined class Parent
scala> class Kid extends Parent
defined class Kid
Create implicits for them too.
scala> implicit val x = Concrete[Parent]
x: Concrete[Parent] = Concrete()
scala> implicit val y = Concrete[Kid]
y: Concrete[Kid] = Concrete()
scala> implicitly[A[Parent]]
res1: A[Parent] = Concrete()
scala> implicitly[A[Kid]]
<console>:21: error: ambiguous implicit values:
both value x of type => Concrete[Parent]
and value y of type => Concrete[Kid]
match expected type A[Kid]
implicitly[A[Kid]]
^
In the first example (without contravariance), Scala was able to resolve the implicit Child for implicitly[Parent]. It seems to me that it's picking the lowest sub-type.
However, when using contravariance, the behavior changes. Why?
Your implicits are typed Concrete and that is invariant here.
Try either
case class Concrete[-T]() extends A[T]
or
implicit val x: A[Parent] = Concrete[Parent]
More words:
Implicits (values or views) should have an explicit type so you're never surprised by the inferred type. Picking the implicit is all about the type.
It picks one of your implicits using the same rules as the overloading resolution conversion that is used to pick alternatives of an overloaded symbol.
For simple values (not function calls), that comes down to conformance or subtyping.
There is also the rule that a definition in a "derived type" (usually a subclass) is preferred.
Here is a test you can do using only commonly available household materials:
scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._, definitions._ also imported **
** Try :help, :vals, power.<tab> **
scala> trait A[-T]
defined trait A
scala> case class Concrete[T](i: Int) extends A[T]
defined class Concrete
scala> class Parent ; class Kid extends Parent
defined class Parent
defined class Kid
// it will pick X if X isAsSpecific as Y but not conversely
scala> typer.infer.isAsSpecific(typeOf[Concrete[Kid]],typeOf[Concrete[Parent]])
res0: Boolean = false
scala> typer.infer.isAsSpecific(typeOf[Concrete[Parent]],typeOf[Concrete[Kid]])
res1: Boolean = false
scala> case class Concrete[-T](i: Int) extends A[T]
defined class Concrete
scala> typer.infer.isAsSpecific(typeOf[Concrete[Kid]],typeOf[Concrete[Parent]])
res2: Boolean = false
scala> typer.infer.isAsSpecific(typeOf[Concrete[Parent]],typeOf[Concrete[Kid]])
res3: Boolean = true
Edit:
Another view of why it matters what type you're testing:
scala> trait A[-T]
defined trait A
scala> case class Concrete[T](i: Int) extends A[T] // invariant
defined class Concrete
scala> class Parent ; class Kid extends Parent
defined class Parent
defined class Kid
scala> implicitly[Concrete[Parent] <:< Concrete[Kid]]
<console>:13: error: Cannot prove that Concrete[Parent] <:< Concrete[Kid].
implicitly[Concrete[Parent] <:< Concrete[Kid]]
^
scala> implicit val x: Concrete[Parent] = Concrete[Parent](3) // the inferred type
x: Concrete[Parent] = Concrete(3)
scala> implicit val y = Concrete[Kid](4)
y: Concrete[Kid] = Concrete(4)
// both values conform to A[Kid] (because A is contravariant)
// but when it puts x and y side-by-side to see which is more specific,
// it no longer cares that you were looking for an A. All it knows is
// that the values are Concrete. The same thing happens when you overload
// a method; if there are two candidates, it doesn't care what the expected
// type is at the call site or how many args you passed.
scala> implicitly[A[Kid]]
<console>:15: error: ambiguous implicit values:
both value x of type => Concrete[Parent]
and value y of type => Concrete[Kid]
match expected type A[Kid]
implicitly[A[Kid]]
^
Give them explicit types and the variance of Concrete won't matter. You always supply explicit types for your implicits, right? Just like retronym tells us to?
scala> implicit val x: A[Parent] = Concrete[Parent](3)
x: A[Parent] = Concrete(3)
scala> implicit val y: A[Kid] = Concrete[Kid](4)
y: A[Kid] = Concrete(4)
scala> implicitly[A[Kid]]
res2: A[Kid] = Concrete(3)