This question already has answers here:
Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work?
(4 answers)
Closed 8 years ago.
class Test1(buf:Buffer[AnyRef])
class Test2(buf:Buffer[String]) extends Test(buf)
Compiler error:
type mismatch;
found : scala.collection.mutable.Buffer[String]
required: scala.collection.mutable.Buffer[Any]
Note: org.msgpack.type.Value <: Any, but trait Buffer is invariant in type A. You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Short answer: You can't add AnyRef to Buffer[String]:
val b: Buffer[AnyRef] = Buffer[String]()
b += new Object // ???
Buffer[String] can't be Buffer[AnyRef] because Buffer[T] is not covariant on type parameter T. It can't be declared covariant (Buffer[+T]) because there is usage of T in contravariant position (for instance in += method).
Related
This question already has answers here:
Why doesn't the example compile, aka how does (co-, contra-, and in-) variance work?
(4 answers)
Closed 7 years ago.
In scala Option class is declared like
sealed abstract class _Option[+A]
case object _None extends _Option[Nothing] {}
final case class _Some[+A](x: A) extends _Option[A] {}
What is [+A]? Why not just [A]? Could it be [-A] and what it would mean?
Sorry if it is a duplicate but I couldn't find the answer on SO.
It declares the class to be covariant in its generic parameter. For your example, it means that Option[T] is a subtype of Option[S] if T is a subtype of S. So, for example, Option[String] is a subtype of Option[Object], allowing you to do:
val x: Option[String] = Some("a")
val y: Option[Object] = x
Conversely, a class can be contravariant in its generic parameter if it is declared as -A.
Read above variances in Scala in the docs here.
When trying to use higher kinded existentials in Scala I run into the following problem:
trait A[H[_]]
trait Test {
val l: A[List]
// [error] type mismatch;
// [error] found : A[List]
// [error] required: A[_[_] <: Any]
// [error] Note: List <: Any, but trait A is invariant in type H.
// [error] You may wish to define H as +H instead. (SLS 4.5)
val x: A[B] forSome { type B[_] } = l
}
Adding a covariant annotation to H as the compiler suggests works. Is there a way to work around this if I don't want H to be covariant?
A slight variation of the example gives a more helpful error message:
scala> l: (A[B] forSome { type B[_] })
<console>:10: error: type mismatch;
found : A[List]
required: A[_[_] <: Any]
Note: List <: Any, but trait A is invariant in type H.
You may wish to define H as +H instead. (SLS 4.5)
l: (A[B] forSome { type B[_] })
^
<console>:10: error: can't existentially abstract over parameterized type B
l: (A[B] forSome { type B[_] })
^
Looking for this error brings us to a TODO in the compiler.
Since existential types are going to disappear, per Odersky's email, I don't think this limitation will be fixed. However, Martin Odersky's email also reminds us that existential types are equivalent to abstract types. Hence, the above example can be encoded as follows:
scala> trait A { type H[_] }
defined trait A
scala> val l: A {type H[X] = List[X]} = null
l: A{type H[X] = List[X]} = null
scala> l: A
res0: A = null
Type application is syntactically ugly, but turning a value into an existential becomes trivial (also to implement in a compiler, which is part of Odersky's point).
What's useful to encode existentials is that type members don't have to be instantiated ever. So, to encode A[_] we can write:
scala> class A { type T }
defined class A
scala> new A
res1: A = A#3a7049a6
What's confusing here is that this does not work for objects:
scala> object B { type T }
<console>:8: error: only classes can have declared but undefined members
object B { type T }
^
But I recently got that accepted as a bug — see here for a pull request clarifying the spec (approved by Adriaan Moors), and here for my bug report and one-line fix to the compiler (still waiting for review).
I am using cascading 2.0 in scala, and meet a weird compilation issue
This is main parts of the code
val sojSource = createSojSource(optionMap("input"))
val sinkScheme = createSojScheme(true)
val sink = new Hfs(sinkScheme, optionMap("output"), SinkMode.REPLACE)
var pipe = new Pipe("soj")
val each = new Each(pipe, new SojSampleFilter())
val flowDef = new FlowDef().addSource(pipe, sojSource).addTailSink(each, sink)// compile error
And here is the error message for the last line of code
type mismatch; found : cascading.tap.hadoop.Hfs required: cascading.tap.Tap[_, _, _]
Note: org.apache.hadoop.mapred.JobConf <: Any (and cascading.tap.hadoop.Hfs <: cascading.tap.Tap[org.apache.hadoop.mapred.JobConf,org.apache.hadoop.mapred.RecordReader,org.apache.hadoop.mapred.OutputCollector]), but Java-defined class Tap is invariant in type Config. You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Note: org.apache.hadoop.mapred.RecordReader <: Any (and cascading.tap.hadoop.Hfs <: cascading.tap.Tap[org.apache.hadoop.mapred.JobConf,org.apache.hadoop.mapred.RecordReader,org.apache.hadoop.mapred.OutputCollector]), but Java-defined class Tap is invariant in type Input. You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Note: org.apache.hadoop.mapred.OutputCollector <: Any (and cascading.tap.hadoop.Hfs <: cascading.tap.Tap[org.apache.hadoop.mapred.JobConf,org.apache.hadoop.mapred.RecordReader,org.apache.hadoop.mapred.OutputCollector]), but Java-defined class Tap is invariant in type Output. You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Could you, please, tell me how can I fix this and what is the reason?
I found this code in the scalding tree which works around this problem:
// The scala compiler has problems with the generics in Cascading
protected def castHfsTap(tap : Hfs) : Tap[JobConf, RecordReader[_,_], OutputCollector[_,_]] = {
tap.asInstanceOf[Tap[JobConf, RecordReader[_,_], OutputCollector[_,_]]]
}
I am confused by the generic subtyping.
In Java, if type A is a subtype of B, generic type C<A> and C<B> are invariant. For instance, ArrayList<Base> is not a subtype of ArrayList<Derived>.
However, in Scala, generic type C<A> and C<B> are covariant if type A is a subtype of B. So what's the property of generic class in Scala has but not in Java?
Firstly note that variance is a property of generic type parameters, not of the parameterized types themselves.
Secondly: you are wrong about scala - type parameters are invariant by default. Let us investigate!
Java
Java has use-site variance annotations. That is, you can declare methods like this:
boolean addAll(Collection<? extends T> c);
There is, however, one form of "parameterized type" (I use the term loosely) in which the type parameters are covariant: Java Arrays! (This is actually insane because Arrays are mutable and hence it is easy to circumvent the type system). Consider the following:
public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }
And then:
Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];
A good interview question this one: what happens?
Scala
In Scala, you have declaration-site variance. That is, the variance of a type parameter is declared alongside the parameter (using the annotations + and -):
trait Function1[-I, +O]
This says that the trait Function1 has two type parameters, I and O which are contra- and co-variant respectively. If no +/- annotation is declared, then the type parameter is invariant. For example, Set is invariant in its type parameter:
scala> def foo(set: Set[Any]) = ()
foo: (set: Set[Any])Unit
scala> Set(1)
res4: scala.collection.immutable.Set[Int] = Set(1)
scala> foo(res4)
<console>:10: error: type mismatch;
found : scala.collection.immutable.Set[Int]
required: Set[Any]
Note: Int <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
foo(res4)
^
List is however, declared as being covariant:
scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit
scala> List(1)
res6: List[Int] = List(1)
scala> bar(res6)
Why not ask the compiler?
Another way of demonstrating this is to ask the compiler directly for subtype-evidence:
scala> class Cov[+A]
defined class Cov
scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>
But with an invariant type parameter
scala> class Inv[A]
defined class Inv
scala> implicitly[Inv[Int] <:< Inv[Any]]
<console>:9: error: Cannot prove that Inv[Int] <:< Inv[Any].
implicitly[Inv[Int] <:< Inv[Any]]
^
Lastly, contravariance:
scala> class Con[-A]
defined class Con
scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>
See also identifier <:<
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.