Scala type bounds & variance
Asked Answered
C

1

6

I am trying to get a better understanding of the following behaviour:

scala> class C[-A, +B <: A]
<console>:7: error: contravariant type A occurs in covariant position
                    in type >: Nothing <: A of type B
       class C[-A, +B <: A]
                    ^

However the following works:

scala> class C[-A, +B <% A]
defined class C

I can see that there could be issues from the variance of the bounding and bounded variables being opposite, though I have am not clear on what the specific problem is. I am even less clear on why changing the type bound to a view bound makes things ok. In the absence of applicable implicit conversions I would expect the two definitions to be have largely the same effect. If anything I would expect a view bound to provide more opportunities for mischief.

For a bit of background I defining classes that are in some ways like functions, and I wanted to do something like

CompositeFunc[-A, +B <: C, -C, +D] (f1 : BaseFunc[A, B], f2 : BaseFunc[C, D]) 
  extends BaseFunc[A, D]

Arguably

CompositeFunc[-A, +B <% C, -C, +D] (f1 : BaseFunc[A, B], f2 : BaseFunc[C, D]) 
  extends BaseFunc[A, D]

is actually preferable, but I would still like to better understand what is going on here.

Cattleman answered 4/6, 2013 at 18:36 Comment(3)
Intriguing... I spent 1 hour looking for an example where type safety breaks with the type bound. None found :(Deckert
Also if there was an example that was unsound with the type bound, it is hard to see how substituting in a view bound would fix it.Cattleman
Yes, I agree somewhat. But the fundamental difference is that the view bound does always convert according to the types the class has been created with, whereas this is not necessarily the case with the type bound. (says my intuition at least...)Deckert
P
4

First the easy one:

class C[-A, +B <% A]

This is equivalent to

class C[-A, +B](implicit view: B => A)

Since view is not publically returned, it is not in a position which would constrain the variance of A or B. E.g.

class C[-A, +B](val view: B => A)  // error: B in contravariant position in view

In other words, C[-A, +B <% A] is no different than C[-A, +B] in terms of constraints, the view argument doesn't change anything.


The upper bound case C[-A, +B <: A] I'm not sure about. The Scala Language Specification in §4.5 states

The variance position of the lower bound of a type declaration or type parameter is the opposite of the variance position of the type declaration or parameter.

The variance of B does not seem to be involved, but generally the upper bound must be covariant:

trait C[-A, B <: A] // contravariant type A occurs in covariant position

This must somehow produce a problem? But I couldn't come up with an example which proofs that this construction becomes unsound in a particular case....


As for the composed function, why not just

class Composite[-A, B, +C](g: A => B, h: B => C) extends (A => C) {
  def apply(a: A) = h(g(a))
}

EDIT: For example:

import collection.LinearSeq

def compose[A](g: Traversable[A] => IndexedSeq[A], h: Traversable[A] => LinearSeq[A]) =
  new Composite(g, h)
Pongee answered 4/6, 2013 at 21:37 Comment(5)
Interesting, thanks. I did not think of trying trait C[-A, B <: A]. I just thought it had to be the opposite variances.Cattleman
Yes they compose, exactly because of the variance of Function1Pongee
class CompositeFunc[-A, B, +C] would not allow you to compose f: BaseFunc(Traversable[A], IndexedSeq[A]) with g: BaseFunc(Traversable[A], LinearSeq[A]) even though their compositions make perfect sense in both orders.Cattleman
Yes @Pongee , f & g should compose as functions, but my classes do not actually extend Function, they are just function like. The whole problem is that CompositeFunc(f, g) would not type check in the with CompositeFunc[-A, B, +C] because of the invariance of B. I was sloppy in my original comment by saying f: Traversable[A] => IndexedSeq[A], so I have rewritten it.Cattleman
It's a pity there is no clear explanation about the C[-A, +B <: A] oneFuss

© 2022 - 2024 — McMap. All rights reserved.