0

In covariance, methods are not allowed to have parameters if the type of the parameters are same as Generic type. This is due to fact that then it becomes feasible to pass super class objects of the type parameter. That is:

class Parent
class Child extends Parent

class Generic[+T] {
   def func(t:T) = ???
}

val x :Generic[Chiild] = new Generic[Child]
val y :Generic[Parent] = x
y.func(new Child)          //This one is illegal; Hence methods are not allowed to have parameters where their type is T.

This is very intuitive & easy to reason. Same thing is said for contravariant where return type is not allowed.

But if we want to make the method in covariance to accept parameters, it can be done in the below manner as per many blogs/articles. I have given the complete example below.

Intuitively, it makes sense if E in the 'func', is sub type of T. Here, if it is Apple or PinkLady, then it makes sense. Because X is initialized with Apple. Due to the same reason, marking covariant type in method parameters is prohibited in my first point (i.e: Fruit is not allowed to be passed).

But here, everything is accepted Ideally when Fruit is passed, it should throw error. I am really confused why Fruit object is accepted. Hence, E must be upper bound (but it is errored out by compiler).

    class Fruit
    class Apple extends Fruit
    class PinkLady extends Apple

class Generic[+T] {
   def func[E >: T](e:E) = "Hello"     //Function definition        
}

val x = new Generic[Apple]

x.func(new Fruit)
x.func(new Apple)
x.func(new PinkLady)

Can some one please help me to understand.

Thanks in advance.

user3103957
  • 636
  • 4
  • 16
  • 1
    `[E >: T](e:E)` is not a very useful function signature. You can do nothing specific with `e`, so `e` could be literally anything. `x.func(42)` works just fine. In order to understand the issue better, define a method in each of the classes, and try using one of the methods on `e` in `func` (otherwise why would you want to pass `e` ?) – n. m. could be an AI Jul 14 '21 at 11:45
  • 1
    Those "blogs/articles" you are referring to, aren't very good. Don't read them. `foo[E>:T](e: E)` is equivalent to `foo(e: Any)` – Dima Jul 14 '21 at 12:32
  • 1
    _"Intuitively, it makes sense if E in the 'func', is sub type of T"_, `E` is NOT a **subtype** of `T`, rather the opposite we are saying that `E` IS a **supertype** of `T`, such that we are saying that the method must be able to handle anything at all, thus it should be able to handle `Parent` types without going through the LISKOV indirection; btw note that your example is bad `y.func(new Child)` is always accepted. - Check this: https://stackoverflow.com/questions/68234835/covariant-type-a-accepted-in-contravariant-position-of-function-argument-a-b/68236182#68236182 – Luis Miguel Mejía Suárez Jul 14 '21 at 14:58
  • Thanks Luis! In that case, we dont need to place any upper/lower bound in the method type, it seems. This page (official Scala page) really confuses me. [ https://docs.scala-lang.org/tour/lower-type-bounds.html ] . We don't need to give any bound; but here it is given. This one makes me mad.. – user3103957 Jul 14 '21 at 16:25
  • @user3103957 _"However, this program does not compile because the parameter elem in prepend is of type B, which we declared covariant. This doesn’t work because functions are contravariant in their parameter types and covariant in their result types"_. Does that answer your question? – Luis Miguel Mejía Suárez Jul 14 '21 at 16:55
  • Yes, but to make the function to accept parameter, lower bound is used in the next section. That's what confuses me... – user3103957 Jul 14 '21 at 17:34
  • @user3103957 _"lower bound is used in the next section"_ yes, but `U >: B` doesn't mean `U` is a **subtype** of `B`, is rather the opposite. - Did you read the answers to the other SO question I linked? One is mine, if you do not understand something there feel free to comment that one I would rather fix that answer so most people can benefit. – Luis Miguel Mejía Suárez Jul 14 '21 at 17:50
  • Thanks Luis! I have posted a question in the other thread. – user3103957 Jul 15 '21 at 08:57

1 Answers1

3

Covariance of T is not important here - it works the same without it (Generic[T]). It's because when passing the parameters to function, the only condition is that it has to be supertype of T - so Any works too. And everything is Any, so you can pass Apple, "your name", or anything else.

amorfis
  • 15,390
  • 15
  • 77
  • 125