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.
Methods in Scala can be parameterized by type as well as by value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.
A context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. Using a context bound, the maximum function of the last section can be written like this: def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
Scala Type HierarchyAny is the supertype of all types, also called the top type. It defines certain universal methods such as equals , hashCode , and toString . Any has two direct subclasses: AnyVal and AnyRef . AnyVal represents value types.
A type class is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing. If you are coming from Java, you can think of type classes as something like java.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With