Looking at ClassTag#runtimeClass, it has a return type of Class[_], i.e. a Class with, as I understand, a wildcard parameter.
I tried to implement a method: A => ClassTag[A]:
import scala.reflect._
scala> def f[A](x: A)(implicit ev: ClassTag[A]) = ev.runtimeClass
f: [A](x: A)(implicit ev: scala.reflect.ClassTag[A])Class[_]
But, the output of the def's definition is, as the docs show, Class[_].
Is it possible to change f such that its return type is Class[A]? If not, then why is it not possible?
Unless you change the signature of f, your only option is to cast the Class[_] to a Class[A].
There is literally only one method in the entire Scala standard library that returns a Class[A], and that is classOf. f[A] cannot be re-written to use classOf[A] since it is a special compiler method and is incompatible with generic type parameters that may not be classes. You would simply get an error:
scala> def f[A: ClassTag](x: A) = classOf[A]
<console>:10: error: class type required but A found
def f[A: ClassTag](x: A) = classOf[A]
^
The best you can get without casting is using x.getClass, but that will return a Class[_ <: A] (no ClassTag needed).
scala> def f[A](x: A): Class[_ <: A] = x.getClass
f: [A](x: A)Class[_ <: A]
scala> f(1)
res8: Class[_ <: Int] = class java.lang.Integer
scala> f(List(1, 2, 3))
res9: Class[_ <: List[Int]] = class scala.collection.immutable.$colon$colon
You might ask, why _ <: A?
The answer to that question is also the reason why your definition of f doesn't really make sense. If A is an Int, it makes sense to be able to return a Class[A] because Int is a class. But what if A is a List[Int]? List[Int] is not a class, it's a type. The class is List, but A != List, therefore we cannot return a Class[A] consistently. We can, however, have an upper-bound of A on the type parameter of Class.
Related
The following code works:
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> implicit val longToInt = (l: Long) => l.toInt
longToInt: Long => Int = $$Lambda$1821/0x000000010086e840#52bd9a27
scala> def printInt(n: Int) = println(n)
printInt: (n: Int)Unit
scala> val opt: Option[Long] = None
opt: Option[Long] = None
scala> val n = opt.getOrElse(0L)
n: Long = 0
scala> printInt(n)
0
However, the compiler throws a type mismatch error if we nest the getOrElse expression inside of the function call:
scala> printInt(opt.getOrElse(0L))
<console>:16: error: type mismatch;
found : AnyVal
required: Int
printInt(opt.getOrElse(0L))
^
Why does opt.getOrElse(0L) have type AnyVal?
Scala 2.12.8
When you write
printInt(opt.getOrElse(0L))
opt.getOrElse(0L) is typed with expected type Int. Option#getOrElse's signature is
getOrElse[B >: A](default: ⇒ B): B
Because it's generic, the compiler tries to find B such that opt.getOrElse[B](0L) has type Int. So B has to satisfy constraints B >: Long (because A in the above signature is Long) and B <: Int (from the return type). Obviously there's no such B. Scala specification says this must be an error, but doesn't say which, and here the compiler happens to report it after guessing B = AnyVal.
Another way to fix it is to specify the expected type for opt.getOrElse(0L):
printInt(opt.getOrElse(0L): Long)
Or the type parameter:
printInt(opt.getOrElse[Long](0L))
The signature of getOrElse is:
def getOrElse[B1 >: B](key: A, default: ⇒ B1): B1
printInt(opt.getOrElse(0L)) tells Scala that B1 ought to be Long, but Long is not a super-type of Int. The common supertype instead is AnyVal. Therefore you cannot make that assignment.
Try casting the result to a Long instead:
printInt(opt.getOrElse(0L).toLong)
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
The following code compile successfully, but the second couldn't compile. As my understanding these 2 symbols both mean type constraint.
class Hello[T <: String](t:T)
class Hello[T <:< String](t:T)
They are a little different. <: is a type constraint meaning A is subtype of B, while <:< is a type meaning the same thing. So this would work:
class Hello[T <: String](t:T)
class Hello[T](t: T <:< String)
alternative
class Hello[T](t: <:<[T, String])
edit:
One use case would be to prove, that one type param is a subtype of the other:
def foo[A,B](a: A, b: B)(implicit ev: A <:< B) = "yay"
scala> foo(Nil, List(1,2,3))
res13: java.lang.String = yay
scala> foo(List(1,2,3), Nil)
<console>:9: error: Cannot prove that List[Int] <:< scala.collection.immutable.Nil.type.
foo(List(1,2,3), Nil)
^
In another question, I'm advised to use with in a place where one normally uses <: or <:<. So instead of defining functions in either of the following two ways:
scala> def f[A,C <: Seq[A]](xs: C) = 0
f: [A, C <: scala.collection.immutable.Seq[A]](xs: C)Int
scala> f(List(1))
<console>:54: error: inferred type arguments [Nothing,List[Int]] do not conform to method f's type parameter bounds [A,C <: scala.collection.immutable.Seq[A]]
f(List(1))
^
scala> implicit def f[A,C](xs: C)(implicit ev: C <:< Seq[A]) = new { def foo = 0 }
f: [A, C](xs: C)(implicit ev: <:<[C,scala.collection.immutable.Seq[A]])java.lang.Object{def foo: Int}
scala> List(0) foo
<console>:54: error: Cannot prove that List[Int] <:< scala.collection.immutable.Seq[A].
List(0) foo
^
scala> f(List(0)) foo
res17: Int = 0
One can do:
scala> implicit def f[A,C](xs: C with Seq[A]) = new { def foo = 0 }
f: [A, C](xs: C with scala.collection.immutable.Seq[A])java.lang.Object{def foo: Int}
scala> List(0) foo
res18: Int = 0
My question is: besides the above particular case, when should one use with instead of <: or <:< on the type parameter? Why not always use with instead? I'm looking for a discussion of the nuances among the alternatives here. Thanks.
The meanings are entirely different. C <: Seq[A] means that C is a subtype of Seq[A], as you know; xs: C with Seq[A] doesn't put any bound on C, but means that xs should be both a C and a Seq[A]. Therefore you should normally use the one you actually mean.
In def f[A,C <: Seq[A]](xs: C) the problem is that Scala's compiler can't infer A because it doesn't appear explicitly in the type of arguments. I don't see any reason in principle it couldn't infer A; it just doesn't currently. Replacing the type with C with Seq[A] means A now appears in the type of xs and allows the compiler to infer A. So if you really mean the bound, but A to be inferred, you actually need to write
implicit def f[A,C <: Seq[A]](xs: C with Seq[A])
instead of your third definition, and this is what the answer to the linked question does.