functioning of covariant in flutter - 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.

Related

Is there any difference in comparing runtimeType versus using X is Y? [duplicate]

On dart tour page (https://dart.dev/guides/language/language-tour#getting-an-objects-type) these have a statement that testing variable type with "is" expression is more stable. Why is it so?
An is test is a subtype test.
If you do e is Iterable, you check whether the value of e implements Iterable<dynamic>, which includes any of List, Set and Queue, as well as all the subtypes of Iterable used internally by the runtime system, like _TakeIterable (which is used to implement Iterable.take).
It matches both values of type Iterable<Object> and Iterable<int>.
All of these are objects which can safely be used as an Iterable, and are intended to be used as such.
If you do e.runtimeType == Iterable, you are checking whether the Type object returned by e.runtimeType is equal to precisely the type Iterable<dynamic>. That will be false for any list, set or queue, for any actual iterable class which only implements Iterable, and even for something which returns the Type object of Iterable<int> or Iterable<Object?> from runtimeType.
I say that you check the object returned by e.runtimeType, not the run-time type of the value, because anyone can override the runtimeType getter.
I can make a class like:
class WFY {
Type get runtimeType => Iterable<int>;
}
void main() {
print(WFY().runtimeType == Iterable<int>); // True!
}
The value returned by runtimeType doesn't have to have any relation to the actual runtime type of the object.
Obviously it usually has, because there is no benefit in overriding runtimeType, because you shouldn't be using it for anything anyway,
Even if your code works today, say:
assert(C().runtimeType == C); // Trivial, right!
it might fail tomorrow if I decide to make C() a factory constructor which returns a subtype, _C implementing C.
That's a change that is usually considered non-breaking, because the _C class can do everything the C interface requires, other than having C as actual runtime type.
So, doing Type object checks is not stable.
Another reason using is is better than comparing Type objects for equality is that it allows promotion.
num x = 1;
if (x is int) {
print(x.toRadixString(16)); // toRadixString is on int, not on num
}
The is check is understod by the language, and trusted to actually guarantee that the value's runtime type implements the type you check against.
Comparing Type objects can mean anything, so the compiler can't use it for anything.
Some people like to use runtimeType in their implementation of ==, like;
class MyClass {
// ...
bool operator ==(Object other) =>
MyClass == other.runtimeType && other is MyClass && this.x == other.x;
}
This is intended to avoid subclass instance being equal to super-class instances when you ask the superclass, but not if you ask the subclass (the "ColorPoint problem", where ColorPoint extends Point with a color, and is equal to a another ColorPoint with the same coordinates and color, but if you ask a plain Point whether it's equal to a ColorPoint, it only checks the coordinates.)
This use of runtimeType "works", but is not without issues.
It means you cannot use mocks for testing.
It means you cannot create a subclass which doesn't extend the state, only the behavior, and which would want to be equal to the superclass instances with the same state.
And it means you do extra work, because you still need to cast the other object from Object to the surrounding type in order to access members, and Type object checks do not promote.
If possible, it's better to never allow subclasss of a concrete class that has a == method, and if you need to share other behavior, inherit that from a shared superclass.
(In other words: Don't extend classes that aren't intended to be extended, don't put == on classes which are intended to be extended.)

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

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.

Scala; Expression of type (X)=>X doesn't conform to type SupX

I'm a scala newbie who play with scala-swing. And want to translate a given scala.swing.Point on a scala.swing.event.MousEvent :
I would like to send mouse event to a class responsible to handle shapes selections. But because a shape has a location relative to his container (sheet) but MouseEvent.point is relative to the window I should translate/relativize it before.
So, I have a Selection class who receive MouseEvents :
case class Selection(sheet:Sheet) {
def on(event:Event) = event match {
case clicked:MouseClicked => {
clicked.modifiers match {
case scala.swing.event.Key.Modifier.Control =>
sheet.getItemAt(clicked.point).map(addToSelection)
case _ =>
sheet.getItemAt(clicked.point).map(setSelection)
}
}
}
}
And a Sheet who his the shapes container (and know how to translate points).
class Sheet extends Component {
private val selection = Selection(this)
listenTo(mouse.clicks, mouse.moves)
reactions += {
case e:MousePressed => selection.on(translate(e))
case e:MouseClicked => selection.on(translate(e))
}
/** Here is my problem :
* > Expression of type (MouseEvent) => MousePressed doesn't conform to expected type Event
*/
def translate(original: MouseEvent): Event = original match {
case pressed:MousePressed =>
pressed.copy(point=relativize(pressed.point))
case clicked:MouseClicked =>
clicked.copy(point=relativize(pressed.point))
case other:MouseEvent=>
other
}
}
I can bypass this problem with an ugly cast :
case pressed:MousePressed =>
pressed.copy(point=relativize(pressed.point)).asInstanceOf[MousePressed]
But then I have another more strange compiler problem :
Error:(32, 21) missing arguments for method copy in class MousePressed;
follow this method with `_' if you want to treat it as a partially applied function
pressed.copy(point = relativize(pressed.point)).asInstanceOf[MousePressed]
And here, I'm lost and need your help to do this simple conversion.
Of course all methods use scala.swing._ types (and never mixes between scala.swing.andjava.awt.`)
Thanks a lot
Regarding the compiler problem, you can understand what's complaining about looking at the MouseEvent (and specifically MousePressed) documentation.
The case class is defined with two parameter lists, with the following simplified signature
MousePressed(source: Component, point: java.awt.Point, modifiers: Modifiers, clicks: Int, triggersPopup: Boolean)(peer: java.awt.event.MouseEvent) extends MouseButtonEvent
As you can see there's a second parameter list expecting the peer object, which is the underlying java swing object. You can access the peer instance using the attribute of the same name (e.g. pressed.peer)
The copy method, generated by the case class definition, probably expects this second parameter, like this
pressed.copy(point = relativize(pressed.point))(pressed.peer)
With the second parameter list missing, the compiler is inferring that you want to partially apply the copy method, thus it suggests you to use the
pressed.copy(point = relativize(pressed.point) _
syntax for partially applying the curried function

What's the rule to implement an method in trait?

I defined a trait:
trait A {
def hello(name:Any):Any
}
Then define a class X to implement it:
class X extends A {
def hello(name:Any): Any = {}
}
It compiled. Then I change the return type in the subclass:
class X extends A {
def hello(name:Any): String = "hello"
}
It also compiled. Then change the parameter type:
class X extends A {
def hello(name:String): Any = {}
}
It can't compiled this time, the error is:
error: class X needs to be abstract, since method hello in trait A of type (name: Any)
Any is not defined
(Note that Any does not match String: class String in package lang is a subclass
of class Any in package scala, but method parameter types must match exactly.)
It seems the parameter should match exactly, but the return type can be a subtype in subclass?
Update: #Mik378, thanks for your answer, but why the following example can't work? I think it doesn't break Liskov:
trait A {
def hello(name:String):Any
}
class X extends A {
def hello(name:Any): Any = {}
}
It's exactly like in Java, to keep Liskov Substitution principle, you can't override a method with a more finegrained parameter.
Indeed, what if your code deals with the A type, referencing an X type under the hood.
According to A, you can pass Any type you want, but B would allow only String.
Therefore => BOOM
Logically, with the same reasonning, a more finegrained return type is allowed since it would be cover whatever the case is by any code dealing with the A class.
You may want to check those parts:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_return_type
and
http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type
UPDATE----------------
trait A {
def hello(name:String):Any
}
class X extends A {
def hello(name:Any): Any = {}
}
It would act as a perfect overloading, not an overriding.
In Scala, it's possible to have methods with the same name but different parameters:
class X extends A {
def hello(name:String) = "String"
def hello(name:Any) = "Any"
}
This is called method overloading, and mirrors the semantics of Java (although my example is unusual - normally overloaded methods would do roughly the same thing, but with different combinations of parameters).
Your code doesn't compile, because parameter types need to match exactly for overriding to work. Otherwise, it interprets your method as a new method with different parameter types.
However, there is no facility within Scala or Java to allow overloading of return types - overloading only depends on the name and parameter types. With return type overloading it would be impossible to determine which overloaded variant to use in all but the simplest of cases:
class X extends A {
def hello: Any = "World"
def hello: String = "Hello"
def doSomething = {
println(hello.toString) // which hello do we call???
}
}
This is why your first example compiles with no problem - there is no ambiguity about which method you are implementing.
Note for JVM pedants - technically, the JVM does distinguish between methods with different return types. However, Java and Scala are careful to only use this facility as an optimisation, and it is not reflected in the semantics of Java or Scala.
This is off the top of my head, but basically for X.hello to fit the requirements of A.hello, you need for the input of X.hello to be a superclass of A.hello's input (covariance) and for the output of X.hello to be a subclass of A.hello's output(contravariance).
Think of this is a specific case of the following
class A
class A' extends A
class B
class B' extends B
f :: A' -> B
g :: A -> B'
the question is "Can I replace f with g in an expression y=f(x) and still typecheck in the same situations?
In that expression, y is of type B and x is of type A'
In y=f(x) we know that y is of type B and x is of type A'
g(x) is still fine because x is of type A' (thus of type A)
y=g(x) is still fine because g(x) is of type B' (thus of type B)
Actually the easiest way to see this is that y is a subtype of B (i.e. implements at least B), meaning that you can use y as a value of type B. You have to do some mental gymnastics in the other thing.
(I just remember that it's one direction on the input, another on the output, and try it out... it becomes obvious if you think about it).

Scala: compare type of generic class

There have been many questions on that issue, but sadly none seems to solve my problem.
I've written a generic scala class, let's call it
class MyClass[A]() { ... }
As well as the according object:
object MyClass() { ... }
Inside MyClass I want to define a function whichs behaviour depends on the given type A. For instance, let's just assume I want to define a 'smaller' function of type (A, A) => Boolean, that by default returns 'true' no matter what the elements are, but is meant to return the correct results for certain types such as Int, Float etc.
My idea was to define 'smaller' as member of the class in the following way:
class MyClass[A]() {
val someArray = new Array[A](1) // will be referred to later on
var smaller:(A,A) => Boolean = MyClass.getSmallerFunction(this)
...some Stuff...
}
object MyClass {
def getSmallerFunction[A](m:MyClass[A]):(A,A) => Boolean = {
var func = (a:Boolean, b:Boolean) => true
// This doesn't compile, since the compiler doesn't know what 'A' is
if(A == Int) func = ((a:Int, b:Int) => (a<b)).asInstanceOf[(A,A) => Boolean)]
// This compiles, but always returns true (due to type erasure I guess?)
if(m.isInstanceOf[MyClass[Float]]) func = ((a:Float, b:Float) => (a<b)).asInstanceOf[(A,A) => Boolean)]
// This compiles but always returns true as well due to the newly created array only containing null-elements
if(m.someArray(0).isInstanceOf[Long]) func = ((a:Long, b:Long) => (a<b)).asInstanceOf[(A,A) => Boolean)]
}
...some more stuff...
}
The getSmallerFunction method contains a few of the implementations I experimented with, but none of them works.
After a while of researching the topic it at first seemed as if manifests are the way to go, but unfortunately they don't seem to work here due to the fact that object MyClass also contains some constructor calls of the class - which, no matter how I change the code - always results in the compiler getting angry about the lack of information required to use manifests. Maybe there is a manifest-based solution, but I certainly haven't found it yet.
Note: The usage of a 'smaller' function is just an example, there are several functions of this kind I want to implement. I know that for this specific case I could simply allow only those types A that are Comparable, but that's really not what I'm trying to achieve.
Sorry for the wall of text - I hope it's possible to comprehend my problem.
Thanks in advance for your answers.
Edit:
Maybe I should go a bit more into detail: What I was trying to do was the implementation of a library for image programming (mostly for my personal use). 'MyClass' is actually a class 'Pixelmap' that contains an array of "pixels" of type A as well as certain methods for pixel manipulation. Those Pixelmaps can be of any type, although I mostly use Float and Color datatypes, and sometimes Boolean for masks.
One of the datatype dependent functions I need is 'blend' (although 'smaller' is used too), which interpolates between two values of type A and can for instance be used for smooth resizing of such a Pixelmap. By default, this blend function (which is of type (A,A,Float) => A) simply returns the first given value, but for Pixelmaps of type Float, Color etc. a proper interpolation is meant to be defined.
So every Pixelmap-instance should get one pointer to the appropriate 'blend' function right after its creation.
Edit 2:
Seems like I found a suitable way to solve the problem, at least for my specific case. It really is more of a work around though.
I simply added an implicit parameter of type A to MyClass:
class MyClass[A]()(implicit dummy:A) { ... }
When I want to find out whether the type A of an instance m:MyClass is "Float" for instance, I can just use "m.dummy.isInstanceOf[Float]".
To make this actually work I added a bunch of predefined implicit values for all datatypes I needed to the MyClass object:
object MyClass {
implicit val floatDummy:Float = 0.0f
implicit val intDummy:Int = 0
...
}
Although this really doesn't feel like a proper solution, it seems to get me around the problem pretty well.
I've omitted a whole bunch of stuff because, if I'm honest, I'm still not entirely sure what you're trying to do. But here is a solution that may help you.
trait MyClass[A] {
def smaller: (A,A) => Boolean
}
object MyClass {
implicit object intMyClass extends MyClass[Int] {
def smaller = (a:Int, b:Int) => (a < b)
}
implicit object floatMyClass extends MyClass[Float] {
def smaller = (a:Float, b:Float) => (a < b)
}
implicit object longMyClass extends MyClass[Long] {
def smaller = (a:Long, b:Long) => (a < b)
}
def getSmallerFunction[T : MyClass](a: T, b: T) = implicitly[MyClass[T]].smaller(a, b)
}
The idea is that you define your smaller methods as implicit objects under your MyClass, object, with a getSmallerFunction method. This method is special in the sense that it looks for a type-class instance that satisfies it's type bounds. We can then go:
println(MyClass.getSmallerFunction(1, 2))
And it automagically knows the correct method to use. You could extend this technique to handle your Array example. This is a great tutorial/presentation on what type-classes are.
Edit: I've just realise you are wanting an actual function returned. In my case, like yours the type parameter is lost. But if at the end of the day you just want to be able to selectively call methods depending on their type, the approach I've detailed should help you.