List of case classes doesn't match T <: Serializable - scala

Given a type definition T <: Serializable, why doesn't this match everything that is serializable, including a List of serializable instances?
I.e. given:
case class Bar()
def foo[T <: Serializable](param1: T) = println(param1)
foo(Bar())
foo(List(Bar()))
The compiler gives the following error:
Error:(6, 2) inferred type arguments [List[A$A15.this.Bar]] do not conform to method foo's type parameter bounds [T <: Serializable]
The case class extends Serializable, so does List - why doesn't a list of Bars match the type?

The case class extends Serializable, so does List
This is true only for Scala 2.12.x (https://issues.scala-lang.org/browse/SI-7402). Prior versions (2.11 and 2.10) did not have the Serializable trait on List. If you use 2.12, your code will compile as expected.
The answer below takes into account the prior version implementation:
List[T] does not extend serializable, It's derives :: and Nil do. You can see this when explicitly using ::.apply
foo(::(1, Nil))
This compiles since the inferred type is :: (or Cons). On the contrary:
foo(1 :: Nil)
Does not because the :: method on List returns a List[A], not ::.
Another thing is that List.apply is always a runtime :: or Nil (because List is abstract), and that's the reason a test such as:
List(1,2,3).isInstanceOf[Serializable]
Yields true. If we check the underlying runtime class, we see:
List(1, 2, 3).getClass.getName
Yields
scala.collection.immutable.$colon$colon

Related

error messages on implicit conversion to value classes

Define a method which accepts types: List[_ <: AnyVal]
def foo(x : List[_ <: AnyVal]) = x
Try to use an AnyRef:
foo(List(new Test))
error: type mismatch;
found : Test
required: AnyVal
Note that implicit conversions are not applicable because they are ambiguous:
both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
are possible conversion functions from Test to AnyVal
Question 1: In the warning message, why does the compiler ignore the other two "generic to AnyVal" implicit conversions defined in Predef.scala?
final implicit class StringFormat[A] extends AnyVal
final implicit class any2stringadd[A] extends AnyVal
Removing the previous ambiguity and forcing compiler to use the ArrowAssoc implicit conversion:
foo(List(new Test -> 1))
error: the result type of an implicit conversion must be more specific than AnyVal
Question 2: What is this error message implying? Its confusing. The method def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y) in the ArrowAssoc class returns a Tuple2 which is of AnyRef type. So, a more useful error message could have been found Tuple2 required AnyVal?
To answer your second question first. Try this:
class Temp
implicit class Arrow[T](a:T) extends Temp{
def -->[B](b:B) = (a,b)
}
def foo(x : List[_ <: Temp]) = x
scala> foo(List(1 --> 2))
res0: List[Temp] = List(Arrow#2e17a321)
This works as expected. It is not complaining because it was searching for Temp and not Tuple2.
Now introduce ambiguity:
implicit class OtherTemp[T](a:T) extends Temp{}
foo(List(1 --> 2)) //compile error
It fails complaining collisions. So to answer your question on why it didn't show AnyRef is because:
Remember ArrowAssoc is being called to get AnyVal representation. With ->, it has a Tuple2 and is trying to retrieve a AnyVal. And because it is unable to retrieve AnyVal, it marks it as error as failure on inability to convert to AnyVal. Its true identity is irrelevant.
For first question:
In my understanding, implicits go as first search basis. So once it finds two and there is ambiguity, it quits complaining. May be this is why it doesn't try with StringFormat etc. This can be confirmed on repl by re-ordering implicit order
implicit class OtherTemp2[T](a:T) extends Temp{
}
implicit class OtherTemp[T](a:T) extends Temp{
}
foo(List[Temp]("asdf"))
Compiler complains as Arrow and OtherTemp2 as collisions. If you re-order and run repl again, it complains based on which implicit was found first.
But I cant find an official source which confirms this.

Use shapeless Mapper without having to specify the result type

This seems to be a classic question for developers used to Scala type-level programming, but I couldn't find (or I don't know how to search for) a solution or pattern for this. Suppose I have a class like this:
abstract class TypedTest[Args <: HList](implicit val optMapper: Mapped[Args, Option]) {
type OptArgs = optMapper.Out
def options: OptArgs // to be implemented by subclasses
}
I want users of this class to instantiate it with an HList type parameter (Args) and the class provides a method to retrieve an HList instance containing an instance of each specified type inside an Option (OptArgs). I'm using shapeless Mapped type class for this. Note that I don't have an instance of Args to provide at instantiation time.
This code doesn't work, as the compiler doesn't infer the concrete type of OptArgs and even an obviously correct implementation such as def options = HNil yields a compilation error. The same code using the Aux pattern:
abstract class TypedTest[Args <: HList, OptArgs <: HList](implicit val optMapper: Mapped.Aux[Args, Option, OptArgs]) {
def options: OptArgs
}
This forces me to specify both lists at instantiation time, which makes the external API needlessly verbose. Is there an workaround for this?
This is my understanding, but I'm not 100% sure and will be happy to stand corrected.
The type member TypedTest.OptArgs is not an abstract type but a type alias. It is the same type for all subclasses of TypedTest – an alias for Mapped[Args, Option].Out, which is an abstract type and cannot be unified with any type but itself. When a subclass is created, the type member OptArgs is not overridden.
It becomes more clear when using Mapped.Aux with an existential type for Out0, which is IIUC more or less equivalent to the above:
abstract class TypedTest[Args <: HList](
implicit val optMapper: Mapped.Aux[Args, Option, T] forSome { type T }) {
type OptArgs = optMapper.Out
def options: OptArgs // to be implemented by subclasses
}
val intTest = new TypedTest[Int :: HNil] {
def options = Some(1) :: HNil
}
Error:(18, 29) type mismatch;
found : shapeless.::[Some[Int],shapeless.HNil]
required: this.OptArgs
(which expands to) T
def options = Some(1) :: HNil
Unfortunately I'm not aware of any possible solution, except adding Out as a type parameter or defining OptArgs as an abstract type and specifying it explicitly in each subclass.

not enough arguments for method toArray

The toArray call in following code does not compile
trait A[T] {
def create:T
def foo(a:Array[Int]) = {
for(b <- a) yield create
}.toArray
}
It throws the following errors:
not enough arguments for method toArray: (implicit evidence$1: scala.reflect.ClassTag[T])Array[T]. Unspecified value parameter evidence$1.
No ClassTag available for T
How do I fix it?
As Sergey said, Java arrays need to know the type of T, but T is eliminated by type erasure.
In scala you can "preserve" a type information at runtime using a ClassTag.
Here's a more in-depth discussion about arrays.
As per fixing it, you need to provide evidence of a ClassTag for T. Here's a possible solution:
import scala.reflect.ClassTag
trait A[T] {
def create: T
def foo(a: Array[Int])(implicit ev: ClassTag[T]) = {
for(b <- a) yield create
}.toArray
}
The implicit ev parameter is filled in automatically by the compiler.
The problem is that you can't create an Array[T] without knowing T. The ClassTag is Scala's way of representing this information. The simple fix would be to change trait A[T] to abstract class A[T: ClassTag] (class is needed because traits can't have any constructor parameters, including implicit ones). If you then create it with a specific type, e.g. class B extends A[Int], the compiler will insert the correct ClassTag itself, with a generic you need to pass the ClassTag through: class C[T: ClassTag] extends A[T].
The simplest way is to remove toArray call. Because you iterate over array so your result will be array too.

How to use shapeless HList to have a list of the same type but with a different generic?

I need to define a type as a list of Action[T >: MyType] where MyType for now is equivalent to Any but it might change.
A List[Action[T]] doesn't fit for many cases and the compiler can't solve it.
So I thought I could use an HList but I'm really noob with it.
I started with type ListOfAction = HList but I want the elements of my list to be all Action but I don't know how to specify that the list should look like Action[Double], Action[Int], Action[String], ... and not only possible type.
You won't be able to encode that as a type. But using shapeless you could write a function that will only accept the type of HList that you mention.
Since you didn't provide your definition for Action, I'll use this one:
case class Action[+T <: MyType](value: T)
And for some MyType inhabitants we'll use this:
trait MyType
case class MyString(string: String) extends MyType
case class MyInt(int: Int) extends MyType
Now using the LUBConstraint from shapeless we can now restrict that all elements of an HList have the same supertype:
def accept[L <: HList](hlist: L)
(implicit ev: LUBConstraint[L, Action[MyType]]) = println(hlist)
Now, you can try this:
accept(Action(MyString("string")) :: Action(MyInt(1)) :: HNil)
While this:
accept(Action(MyString("string")) :: 1 :: HNil)
Will fail to compile.
Note that you need to define the T in Action as +T and changed the :> to <:, otherwise you won't get the subtype relation.

Understanding List[+A] for Covariance

Looking at the source for List.scala:
sealed abstract class List[+A] extends ...
...
def isEmpty: Boolean
def head: A
def tail: List[A]
List[+A] is covariant based on the +A. Does this mean that, it's possible to create a List[T] where T can be the type itself, or any of its sub-classes?
example:
scala> trait Kid
defined trait Kid
scala> case class Boy(name: String) extends Kid
defined class Boy
scala> case class Girl(name: String) extends Kid
defined class Girl
scala> val list: List[Kid] = List(Boy("kevin"), Girl("sally"))
list: List[Kid] = List(Boy(kevin), Girl(sally))
Observe that head and tail's types are A and List[A], respectively. Once we've defined List[+A], then head and tail's A is also covariant?
I've read this StackOverflow answer 3 or 4 times, but I don't understand yet.
Your example does not relate to variance. Moreover, head and tail have nothing to do with variance too.
scala> val list: List[Kid] = List(Boy("kevin"), Girl("sally"))
list: List[Kid] = List(Boy(kevin), Girl(sally))
This would work even if List weren't covariant, because Scala will automatically deduce common supertype of Boy and Girl, that is, Kid, and type of the expression on the right side will be List[Kid], exactly what you require on the left side.
The following, however, doesn't work because java.util.List is not covariant (it is invariant since it is Java type):
scala> import java.util.{List => JList, Arrays}
import java.util.{List=>JList, Arrays}
scala> trait Kid
defined trait Kid
scala> case class Boy(name: String) extends Kid
defined class Boy
scala> val list1 = Arrays.asList(Boy("kevin"), Boy("bob"))
list1: java.util.List[Boy] = [Boy(kevin), Boy(bob)]
scala> val list2: JList[Kid] = list1
<console>:12: error: type mismatch;
found : java.util.List[Boy]
required: java.util.List[Kid]
Note: Boy <: Kid, but Java-defined trait List is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: Kid`. (SLS 3.2.10)
val list2: JList[Kid] = list1
^
Arrays.asList method has signature like this:
def asList[T](args: T*): java.util.List[T]
As java.util.List[T] is invariant, it is impossible to assign JList[Boy] (list1) to JList[Kid] (list2). And there is a reason: if you could, then because JList is mutable, you could also add anything extending Kid (not only Boy) into the same list, breaking type safety.
On the other hand, scala.List will work in exactly the same situation:
scala> val list1 = List(Boy("kevin"), Boy("bob"))
list1: List[Boy] = List(Boy(kevin), Boy(bob))
scala> val list2: List[Kid] = list1
list2: List[Kid] = List(Boy(kevin), Boy(bob))
That is because scala.List is covariant in its type parameter. Note that covariant List type works as if List[Boy] were subtype of List[Kid], very similar to the case when you can assign everything to a variable of type Any because every other type is a subtype of Any. This is very helpful analogy.
Contravariance works in a very similar way, but in other direction. Consider this trait:
trait Predicate[-T] {
def apply(obj: T): Boolean
}
object Predicate {
// convenience method to convert functions to predicates
def apply[T](f: (T) => Boolean) = new Predicate[T] {
def apply(obj: T) = f(obj)
}
}
Note the - before T parameter: it is a contravariance annotation, that is, Predicate[T] is defined to be contravariant in its only type parameter.
Recall that for covariant list List[Boy] was a subtype of List[Kid]. Well, for contravariant predicate it works in the opposite way: Predicate[Kid] is a subtype of Predicate[Boy], so you can assign a value of type Predicate[Kid] to a variable of type Predicate[Boy]:
scala> val pred1: Predicate[Kid] = Predicate { kid => kid.hashCode % 2 == 0 }
pred1: Predicate[Kid] = Predicate$$anon$1#3bccdcdd
scala> val pred2: Predicate[Boy] = pred1
pred2: Predicate[Boy] = Predicate$$anon$1#3bccdcdd
If Predicate[T] weren't contravariant, we wouldn't be able to assign pred1 to pred2, though it is completely legitimate and safe: obviously, predicates defined on supertypes can easily work on subtypes.
In short, variance affects type compatibility between parameterized types. List is covariant, so you can assign a value of type List[Boy] to a variable of type List[Kid] (in fact, for any T extending S, you can assign a value of type List[T] to a variable of type List[S]).
On the other hand, because, Predicate is contravariant, you can assign Predicate[Kid] to Predicate[Boy] (that is, for any T extending S, you can assign a value of type Predicate[S] to a variable of type Predicate[T]).
If a type is invariant in its type parameter, neither of the above can be done (as is demonstrated by JList).
Note the correspondence between parameterized types and their parameters:
T <: S ===> List [T] <: List [S] (covariance)
T <: S ===> Predicate[S] <: Predicate[T] (contravariance)
This is the reason why the first effect is called *co*variance (T <: S on the left, and
..T.. <: ..S.. on the right), and the second is *contra*variance (T <: S on the left, but ..S.. <: ..T.. on the right).
Whether to make your own parameterized types covariant or contravariant or invariant depends on your class responsibilities. If it may only return values of generic type, then it makes sense to use covariance. List[T], for example, only contains methods which return T, never accept T as a parameter, so it is safe to make it covariant in order to increase expressiveness. Such parameterized types can be called producers.
If your class only accepts values of the generic type as a parameter, not returns them (exactly like Predicate above which has single method def apply(obj: T): Boolean), then you can safely make it contravariant. Such parameterized types can be called consumers
If your class both accepts and returns values of the generic type, i.e. it is both a producer and a consumer, then you have no choice but to leave the class invariant in this generic type parameter.
This idiom is usually called "PECS" ("Producer extends, Consumer super") because variance annotations are written extends and super in Java.