Why can't class-wide upper-bound constraints be covariant and lower-bound constraints be contravariant in Hack? - covariance

Regardless of the variance of parameter on the left side, the constraints placed on Ta and Tb in the following declaration fail the typecheck:
class A<+TCov, -TCon, [±]Ta as TCov, [±]Tb super TCon> {
public function __construct(private Ta $ta, private Tb $tb) {}
// [various methods making use of Ta and Tb]
}
It's worth noting that the empty class declaration doesn't raise errors, but once the constrained parameters are used (in otherwise valid positions given their own variances), the typechecker raises one of the following:
Illegal use of covariant type parameter (Typing[4120])... as constraints are contravariant
Illegal use of contravariant type parameter (Typing[4121])... super constraints are covariant
with reference to the parameter on the right side of the constraint.
I can more understand why generic methods pose problems. The violating positions are fairly obvious, and using the arguments in positions matching the variance of their constraints are impossible:
class A<+TCov, -TCon> {
public function cov_violate<T as TCov>(T $v): void {
// T can be cast to TCov and violate type if the original type is a subtype of T
}
public function con_violate<T super TCon>(): T {
// vice versa for the contravariant parameter
}
public function cov_impossible<T as TCov>(): T {
// how will we produce a T-typed value?
}
public function con_impossible<T super TCov>(T $v): void {
// what will we do with a T-typed value?
}
}
But what is the problem with class-wide parameters? For all six errant relationships ({+|-| }T as +TCov and {+|-| }T super -TCon) I can't think up a scenario where these wouldn't be type-safe. In my mind, their variances seem to either restrict their casting direction or their positions sufficiently to let declaring these relationships be safe.

I was running 3.13.1 at the time of this question, but luckily the restriction has been relaxed to allow this subtyping of class type parameters as of HHVM 3.14.4 by this commit! The commit also points to this Microsoft paper for a proof of soundness.

Related

functioning of covariant in flutter

I was going through dart documentation and there I came across this code and this term
covariant. I went through some documentation but I didn't get what is its function there. A detailed explained answer is always appreciated.
class Animal {
void chase(Animal x) { ... }
}
class Mouse extends Animal { ... }
class Cat extends Animal {
#override
void chase(covariant Mouse x) { ... }
}
In Dart, if you override a superclass method, the arguments of the override method must have the
same type as the original.
Since Animal.chase in your example accepts an argument of Animal, you must do the same in your override:
class Cat extends Animal {
#override
void chase(Animal x) { ... }
}
Why? Imagine if there was no such restriction. Cat could define void chase(Mouse x) while Dog could
define void chase(Cat x). Then imagine you have a List<Animal> animals and you call chase(cat) on
one of them. If the animal is a dog, it'll work, but if the animal is cat, Cat is not a Mouse! The Cat
class has no way to handle being asked to chase another Cat.
So you're forced to use void chase(Animal x). We can simulate a void chase(Mouse x) type signature
by adding a runtime type check:
void chase(Animal x) {
if (x is Mouse) {
/* do chase */
} else {
/* throw error */
}
}
It turns out this is a fairly common operation, and it would be nicer if it could be checked at compile time
where possible. So Dart added a covariant operator. Changing the function signature to chase(covariant Mouse x)
(where Mouse is a subclass of Animal) does three things:
Allows you to omit the x is Mouse check, as it is done for you.
Creates a compile time error if any Dart code calls Cat.chase(x) where x is not a Mouse or its subclass — if known at compile time.
Creates a runtime error in other cases.
Another example is the operator ==(Object x) method on objects. Say you have a class Point:
You could implement operator== this way:
class Point {
final int x, y;
Point(this.x, this.y);
bool operator==(Object other) {
if (other is Point) {
return x == other.x && y == other.y;
} else {
return false;
}
}
}
But this code compiles even if you compare Point(1,2) == "string" or a number or some other object. It makes no sense to compare a Point with things that aren't Points.
You can use covariant to tell Dart that other should be a Point, otherwise it's an error. This lets you drop the x is Point part, too:
bool operator==(covariant Point other) =>
x == other.x && y == other.y;
Why is it called 'covariant'?
Covariant is a fancy type theory term, but it basically means 'this class or its subclasses'. Put another way, it means types
that are equal or lower in the type hierarchy.
You are explicitly telling Dart to tighten the type checking of this argument to a subclass of the original.
For the first example: tightening Animal to Mouse; for the second: tightening Object to Point.
Useful related terms are contravariant, which means types equal or higher in the type hierarchy, and invariant,
which means exactly this type.
For more information, this Stack Overflow question is a good resource.
Just try to remove the key word covariant and it will become self explanatory.
You will receive a compiler error that you are overiding a method with mismatch parameter type Expected: Animal, Actual: Mouse
However, Mouse is a subtype of Animal, so if you want to allow this case without error, add the covariant keyword
Before
After
Here you can see the Mouse is subtype of animal
By using the covariant keyword, you disable the type-check and take responsibility for ensuring that you do not violate the contract in practice.
As you can see in the example, if you are overriding a method, its params should also be the same. But if you are using covariant, it will allow you to use Mouse instead of Animal.

Why in Covariant class in Scala var in constructor is forbidden? [duplicate]

This question already has answers here:
covariant type A occurs in contravariant position in type A of value a
(5 answers)
Closed 5 years ago.
I have class
class GenericClass[+T] (var x: T) {}
When I try to compile it I get:
Error:(6, 33) covariant type T occurs in contravariant position in type T of value x_=
class GenericCellImm[+T] (var x: T) {
How to fix it? What is the problem reason?
Covariant generic type means that if you have classes Base and Child that extends Base, then in every context where GenericClass[Base] is a correct type using an object of type GenericClass[Child] will also be correct. As a rough approximation it means that if your GenericClass[T] provides read-only API, you may make +T covariant because it is always safe to return Child instead of Base. However this is not true if you have some writeable API in your GenericClass[T]. Consider following code:
def writeDefault(gc: GenericClass[Base]):Unit = {
gc.x = new Base()
}
This code is safe if gc is really of type GenericClass[Base] but if you let GenericClass[Child] there, you'll write a value of type Base to a field that is typed with Child and this breaks expected type safety. Consider
class Base(){}
class Child(childField:Int) extends Base {}
def bad():Unit = {
val gc = new GenericClass[new Child(1)]
writeDefault(gc)
println(gc.childField) // Oops! there is no childField in Base
}
And this is exactly why the compiler doesn't allow your code with covariance.
If you want get some real suggestion on how to fix it, you should describe the higher-level problem you are trying to solve. Without such a description it is really hard to guess whether just using val instead of var is what you need or whether you just need to remove covariance altogether or if you need a more complicated code.

When should use covariance and contravariance when designing a class with generic type

I'm learning functional programming and I'm trying to understand covariance and contravariance concept. My problem now is: I don't really know when should apply covariance and contravariance to a generic type. In specific example, yes, I can determine. But in general case, I don't know which general rule.
For example here is some rules that I have studied:
if generic type acts as parameter: using contravariance. (1)
if generic type acts as retrun value: using covariance. (2)
In some languages that I known while stuyding this concept also use those convention. For example: in keyword for covariance(In Scala is +) and out keyword for contravariance (in Scala is -). Point (1) is easy to understand. But at point (2), I see exception:
methodA(Iterator<A>) A should be covariance
methodA(Comparator<A>) A should be contravariance.
So the exception here is: although both 2 cases using generic type as input but one should be covariance and other should be contravariance. My question is: do we have any general rule to decide covariance/contravariance when designing a class.
Thanks
Covariance and contravariance are like the signs of numbers in arithmetic, and when you nest a position with variance in a another, the composition is different.
Compare:
1] +(+a) = +a
2] -(+a) = -a
3] +(-a) = -a
4] -(-a) = +a
and
trait +[+A] { def make(): A } // Produces an A
trait -[-A] { def break(a: A) } // Consumes an A
1]
// Produces an A after one indirection: x.makeMake().make()
trait ++[+A] { def makeMake(): +[A] }
+[+[A]] = +[A]
2]
// Consumes an A through one indirection: x.breakMake(new +[A] { override def make() = a })
trait -+[-A] { def breakMake(m: +[A]) }
-[+[A]] = -[A]
3]
// Consumes an A after one indirection: x.makeBreak().break(a)
trait +-[-A] { def makeBreak(): -[A] }
+[-[A]] = -[A]
4]
// Produces an A through one indirection
// Slightly harder to see than the others
// x.breakBreak(new -[A] { override def break(a: A) = {
// you have access to an A here, so it's like it produced an A for you
// }})
trait --[+A] { def breakBreak(b: -[A]) }
-[-[A]] = +[A]
So when you have
def method(iter: Iterator[A])
the method parameter as a whole is in a contravariant position, and the A is in a covariant position inside the Iterator, but -[+[A]] = -[A], so A is actually in a contravariant position inside this signature and the class needs to say -A. This makes sense, the outside user is passing in a bunch of As, so it ought to contravariant.
Similarly, in
def method(comp: Comparator[A])
the whole method parameter is in a contravariant position, and A is in a contravariant position inside the Comparator, so you compose them -[-[A]] = +[A] and you see that A is really in a covariant position. This also makes sense. When you pass an A into the Comparator, the outside user has control over what it does, and so it's a little like returning that A to them.

How to understand the two sentences about "Covariance" and "Contravariance"?

I'm reading the first section of "Scala in depth", there are two sentences in the first section about "covariance" and "contrvariance":
Covariance (+T or ? extends T) is when a type can be coerced down the inheritance hierarchy.
Contravariance(-T or ? super T) is when a type can be coerced up the inheritance hierarchy.
I have read some documents about "Covariance" and "Contravariance", but I can't understand the word "coerced down" and "coerced up" here in this context.
[TOP / ABSTRACT]
Thing
↓
Animal
↓
Human
↓
Programmer
↓
Scala Dev
[BOTTOM / SPECIFIC]
Covariance: Accept T or lower.
I asked for a [+Human], I will accept any of these: [Human, Programmer, Scala Dev].
Contravariance: Accept T or higher.
I asked for a [-Human], I will accept any of these: [Thing, Animal, Human].
Inariance: Accept T and only T.
Coercion.
Coercing a type up/down the type hierarchy means checking that a type's super/sub type passes type constraints. For example, a covariant function needs a Human but we've only got a Programmer, that's ok, the compiler can coerce the Programmer into Human to satisfy typing constraints.
In this case coerced means the compiler can treat the type as a type further up/down the inheritance hierarchy.
Think of it as upcasting or downcasting except the compiler is doing it automatically, so it is not a cast (which could suggest that explicit code was required to perform it).
This response is taken from lectures given by Martin Odersky (the creator of Scala) on Coursera. We note:
S<:T means: S is a subtype of T, and
S>:T means: S is a supertype of T, or T is a subtype of S.
Say C[T] is a parameterized type and A, B are types such that A<:B.
In general there is three possible relationships between C[A] and C[B]:
C[A]<:C[B] ---------> C is covariant
C[A]>:C[B] ---------> C is contravariant
neither C[A] nor C[B] is a subtype of the other ---------> C is nonvariant
Scala lets you declare the variance of a type by annotating the type parameter:
class C[+A] { ... } ---------> C is covariant
class C[-A] { ... } ----------> C is contravariant
class C[A] { ... } ----------> C is nonvariant
Hope this might help!

Scala type parameter bracket

I know trait Foo[T] means T is a parametrized type.
But some times I can see trait Foo[T1,T2], or trait Foo[T1,T2,R], I cannot find anywhere describe the meaning of multiple types inside a type bracket, could you please point me the usages in this case? From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.
When I read playframework documentation today, I again found myself confused about this question. In the documentation, it says:
A BodyParser[A] is basically an Iteratee[Array[Byte],A], meaning that
it receives chunks of bytes (as long as the web browser uploads some
data) and computes a value of type A as result.
This explanation sounds like, the second the type parameter inside a type bracket is a return type.
I also remember that trait Function2 [-T1, -T2, +R] extends AnyRef means a function that takes a T1 and T2, return a R.
Why do they put the return type in the bracket? Does it mean all the last parameter in a bracket is a return type? Or they just happened defined a new type R for the return type?
From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.
A type parameter means nothing more than "I need any type but I'm not interested to know what it is for a concrete type", where 'I' is the programmer who writes the code. Type parameters can used like any other types such as Int, String or Complex - the only difference is that they are not known until one uses them.
See type Map[A, +B]. When you first read this, you can't know what the A and B are for, thus you have to read the documentation:
A map from keys of type A to values of type B.
It explains the types and their meaning. There is nothing more to know or understand. They are just two types. It is possible to call Map something like Map[Key, Value] but inside of source code it is better when type parameters have only one or two letters. This makes it easier to differ between the type parameters and concrete types.
It is the documentation which specifies what a type parameter means. And if there is no documentation you have to take a look to the sources and find their meaning by yourself. For example you have to do this with Function2 [-T1, -T2, +R]. The documentation tells us only this:
A function of 2 parameters.
Ok, we know that two of the three type parameters are parameters the function expects, but what is the third one? We take a look to the sources:
def apply(v1: T1, v2: T2): R
Ah, now we know that T1 and T2 are the parameters and that R is a return type.
Type parameters also can be found in method signatures like map:
class List[+A] {
..
def map[B](f: (A) ⇒ B): List[B]
}
This is how map looks like when you use it with a List. A can be any type - it is the type of the elements a list contains. B is another arbitrary type. When you know what map does then you know what B does. Otherwise you have to understand map before. map expects a function which can transform each element of a List to another element. Because you know that A stands for the elements of the List you can derive from yourself that B have to be the type A is transformed to.
To answer all your other questions: This shouldn't be done in a single answer. There are a lot of other questions and answers on StackOverflow which can also answer your questions.
Summary
When you see some type parameters for example in Foo[T1, T2] you should not start to cry. Think: "Ok, I have a Foo which expects a T1 and a T2 and if I want to know what they do I have to read documentation or the sources."
Multiple types inside a type bracket means type parametrization on multiple types. Take for example
trait Pair[A, B]
This is a pair of values one having type A the other having type B.
Update:
I think you are interpreting too much into the semantics of type parameters. A type parametrized by multiple parameters is just that and nothing more. The position of a specific type parameter in the list of type parameters does not make it special in any way. Specifically the last parameter in a list of type parameters does not need to stand for 'the return type'.
The sentence from the play framework which you quoted explains the semantics of the type parameters for this one specific type. It does not generalize to other types. The same holds for the Function types: here the last type parameter happens to mean 'the return type'. This is not necessarily the case for other types though. The type Pair[A, B] from above is such an example. Here B is the type of the second component of the pair. There is no notion of a 'return type' here at all.
Type parameters of a parametrized type can appear anywhere inside the definition of the parametrized type where a 'regular' type could appear. That is, type parameters are just names for types which are bound to the actual types only when the parametrized type itself is instantiated.
Consider the following definition of a class Tuple:
class Tuple[A, B](a: A, b: B)
It is instantiated to a type of a tuple of Int and String like this:
type TupleIntString = Tuple[Int, String]
Which is essentially the same as
class TupleIntString(a: Int, b: String)
For an official source check the Scala Language Specification. Specifically Section 3.4 "Base Types and Member Definitions" under 1. the 4th bullet point says: "The base types of a parameterized type C[T_1, ..., T_n] are the base types of type C , where every occurrence of a type parameter a_i of C has been replaced by the corresponding parameter type T_i."
I think your question can actually be broken in three separate problems:
What's with the multiple type parameters for classes/traits/etc. ?
A classic example is a map from one type of object to another. If you want the type for the keys to be different from type of the value, but keep both generic, you need two type parameters. So, a Map[A,B] takes keys of generic type A and maps to values of generic type B. A user that wants a map from Bookmarks to Pages would declare it as Map[Bookmark, Page]. Having only one type parameters would not allow this distinction.
From what I speculate, Foo[T1,T2] just means, it defined two type parameters, it doesn't have to be take a T1 and return a T2.
No, all type parameters are equal citizens, though they have a meaning in that direction for function objects. See below.
What are all those +/-'s ?
They limit what the type parameters can bind to. The Scala by Example tutorial has a good explanation. See Section 8.2 Variance Annotations.
What is a function in scala?
Why do they put the return type in the bracket? Does it mean all the
last parameter in a bracket is a return type? Or they just happened
defined a new type R for the return type?
The Scala by Example tutorial explains this well in Section 8.6 Functions.
Their role is a bit similar to the ones (i.e. multiple type parameter) in a class, since traits are, after all, classes (without any constructor) meant to be added to some other class as a mixin.
The Scala spec gives the following example for Trait with multiple parameters:
Consider an abstract class Table that implements maps from a type of keys A to a type of values B.
The class has a method set to enter a new key/value pair into the table, and a method get that returns an optional value matching a given key.
Finally, there is a method apply which is like get, except that it returns a given default value if the table is undefined for the given key. This class is implemented as follows.
abstract class Table[A, B](defaultValue: B) {
def get(key: A): Option[B]
def set(key: A, value: B)
def apply(key: A) = get(key) match {
case Some(value) => value
case None => defaultValue
}
}
Here is a concrete implementation of the Table class.
class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) {
private var elems: List[(A, B)]
def get(key: A) = elems.find(._1.==(key)).map(._2)
def set(key: A, value: B) = { elems = (key, value) :: elems }
}
Here is a trait that prevents concurrent access to the get and set operations of its
parent class
trait Synchronized Table[A, B] extends Table[A, B] {
abstract override def get(key: A): B =
synchronized { super.get(key) }
abstract override def set((key: A, value: B) =
synchronized { super.set(key, value) }
}
Note that SynchronizedTable does not pass an argument to its superclass, Table,
even though Table is defined with a formal parameter.
Note also that the super calls in SynchronizedTable’s get and set methods statically refer to abstract methods in class Table. This is legal, as long as the calling method is labeled abstract override (§5.2).
Finally, the following mixin composition creates a synchronized list table with
strings as keys and integers as values and with a default value 0:
object MyTable extends ListTable[String, Int](0) with SynchronizedTable
The object MyTable inherits its get and set method from SynchronizedTable.
The super calls in these methods are re-bound to refer to the corresponding implementations in ListTable, which is the actual supertype of SynchronizedTable in
MyTable.