auto mix api when self typing in Scala - scala

This is somehow unexpected in scala(or my expectation is wrong). So following are some sample code:
trait A
trait B
trait HasA { def foo: A }
trait HasB { def foo: B }
so far so good, then i do
scala> trait HasBoth extends HasA with HasB
<console>:11: error: overriding method foo in trait HasA of type => A;
method foo in trait HasB of type => B has incompatible type
trait HasBoth extends HasA with HasB
Alright, I see the error. it makes sense.
but the confusion comes following:
trait Wrapper[T] { def get: T }
trait HasBoth2 { self: Wrapper[HasA with HasB] => def bar = get.foo }
this I found it's insane. if we check the type of bar, its B. if we flip the order, such that the constraint is HasB with HasA, then bar is A.
so we have some inconsistency here. I would expect either of:
HasA with HasB yield { def foo: A with B } automatically, or
HasBoth2 yields a type incompatibility error.
please kindly explain this inconsistency, or it's a type system bug.
Update
trait HasBoth3 extends HasA with HasB { def foo: A with B }
type Wrapped = Wrapper[HasBoth3]
Wrapped will satisfy the constraint of HasBoth2.

This is the expected behavior. It is one of scala's core points of unsoundness.
SI-7278: Unsoundess in many guises
I offer some explanation in SI-7255, reproduced in part below.
This seems to be a pretty fundamental failure of the type system, and maybe part of the motivation for dotty.
Abstract type members aren't unified as normally implied by the word; they can only be refined. What that means in this context is that a type is created which is the intersection of B with its self type, that is, B with A. The intersection type is created assuming it can just use the last 'C' in the linearization, since the compile will fail in refchecks unless the last 'C' is a refinement of any earlier 'C's.
This system breaks down with self types, because a class sees itself as having the intersection of the declared class and the declared self type. Either X with Y or Y with X might be valid instantiations; there is no way a priori to know which is the "last" member C.
For yet more color, see my last comment on SI-7472.

Related

Why scala self type is not a subtype of its requirement

I wonder what is the reasoning behind the following behaviour?
# trait Bar
defined trait Bar
# trait Foo { self: Bar => }
defined trait Foo
# def x: Foo = ???
defined function x
# val y: Bar = x
cmd3.sc:1: type mismatch;
found : ammonite.$sess.cmd1.Foo
required: ammonite.$sess.cmd0.Bar
val y: Bar = x
^
Compilation Failed
AFAIU, Foo requires each of its subtypes to be a subtype of Bar so why instance of Foo is not a proper instance of Bar?
#Edit
Just to make the question clearer: I wonder why it works like that. Some possible answers are:
There is feature X that would not be possible with subtyping relation between those.
It's not true that the subtyping relation occurs (e.g. exists such instance of type Foo which is not the instance of type Bar in runtime)
Both scenarios (with and without subtyping relation) are valid and so the compiler team had to choose one of them. If so, was there a reason to make such a decision or it was a random choice?
It seems that at least 1) is somewhat true (hiding subtyping as implementation detail).
Because a selftype is an implementation detail of the Foo trait. You may want to implement Foo's methods in terms of methods that you inherit from Bar, but not expose that fact to the users of your API.
Back in 12-Mar-2006 Scala 2.0 introduced new keyword
requires
which was used to represent self types
class C requires T extends B { ... }
requires keyword was eventually deprecated on 27-Jul-2007 in Scala 2.6.0 in favour of the current self type syntax
The requires clause has been deprecated; use { self: T =>; ... }
instead
however it serves as a good indicator of the design intention of the self type which was to model relationship of "Bar requires Foo" as opposed to "Bar is Foo", as per Daniel
Now, as to what is the difference between a self type and extending a
trait, that is simple. If you say B extends A, then B is an A. When
you use self-types, B requires an A.
For example, programer requires coffee, but that does not necessarily mean programer is coffee (although I would not be surprised if few lost souls managed the transition).
Furthermore, bug issue Can't access public members despite bounded 'this' #9718, which is similar to OP, was closed with #retronym (a compiler contributor) stating
The current behaviour is as specified and actually a feature: the
self type is an implementation detail of Bar that should not be
visible to clients.
AFAIU, Foo requires each of its subtypes to be a subtype of Bar
No it doesn't.
def test[T <: Foo]: Unit = {
implicitly[T <:< Bar] // doesn't compile
}
I defined a subtype of Foo, namely T, which is not a subtype of Bar.
This is true for subclasses.
class Impl extends Foo with Bar
If a class extends Foo it must extend Bar too.
Types and classes are different. Subtypes and subclasses are different. Subtyping and inheritance are different.
In
trait Foo { self: Bar => }
Foo is not a subtype of Bar. So you can't assign value of type Foo to variable of type Bar
def x: Foo = ???
val y: Bar = x // doesn't compile
If you want to make Foo a subtype of Bar you should use inheritance
trait Foo extends Bar
def x: Foo = ???
val y: Bar = x // compiles
or subtyping
type Foo <: Bar
def x: Foo = ???
val y: Bar = x // compiles
For example with self-types you can define cyclic dependencies:
trait Bar { self: Foo => }
trait Foo { self: Bar => }
class Impl extends Foo with Bar
If trait A { self: B => } implied that A <: B then we would have in such case that Bar <: Foo and Foo <: Bar, so Bar =:= Foo but it's not true, types of those traits are different.
trait A { self: B => } could mean that A <: B (and in such case either we wouldn't have cyclic dependencies or such traits would have equal types) but there is no necessity in that: if you need A <: B you can just declare A extends B while trait A { self: B => } has a different meaning: all subclasses (not subtypes) of A are subtypes of B.
Or you can limit implementation
trait Foo { self: Impl => }
class Impl extends Foo
(Impl can be the only implementation of trait Foo like making Foo sealed with the only inheritor) but there is no need to make types of Foo and Impl the same.
Let's consider also the following example
trait NatHelper {
//some helper methods
}
sealed trait Nat { self: NatHelper =>
type Add[M <: Nat] <: Nat
}
object Zero extends Nat with NatHelper {
override type Add[M <: Nat] = M
}
class Succ[N <: Nat] extends Nat with NatHelper {
override type Add[M <: Nat] = Succ[N#Add[M]]
}
Notice that the abstract type Nat#Add[M] is a subtype of Nat but there is no need to make it a subtype of NatHelper.
Types are not necessarily connected with runtime stuff. Types can have independent meaning. For example they can be used for type-level programming when you formulate your business logic in terms of types.
Also there are so called tagged types (or phantom types) when you attach some information to a type
val x: Int with Foo = 1.asInstanceOf[Int with Foo]
Here we attached "information" Foo to number 1. It's the same runtime number 1 but at compile time it's enriched with "information" Foo. Then x.isInstanceOf[Bar] gives false. I'm not sure you'll accept this example since we use asInstanceOf but the thing is that you can use some library function
val x: Int with Foo = 1.attach[Foo]
and you will not know that it uses asInstanceOf under the hood (as often happens), you will just trust its signature that it returns Int with Foo.

Higher kinded type constructor with upper type bounds doesn't work if bound is abstract type member

I want to define a trait that is parameterized by an upper bound R and a higher kinded type constructor F[_] that accepts only arguments that are subtypes of R. I want that this trait implements a polymorphic apply that can transform any F[A] into Unit, provided that A <: R.
This code works perfectly fine:
import scala.language.higherKinds
// this is the trait with polymorphic `apply`
trait CoCone[R, F[_ <: R]] {
def apply[A <: R](x: F[A]): Unit
}
// Example:
sealed trait Domain
class Dom1 extends Domain
class Fnctr[X <: Domain]
val c = new CoCone[Domain, Fnctr] {
def apply[D <: Domain](x: Fnctr[D]): Unit = ()
}
(see remark about the naming below)
Now, if I abstract over the R by declaring it a type member of some module, and define Fnctr[A <: R] inside this module, like this:
import scala.language.higherKinds
trait CoCone[R, F[_ <: R]] {
def apply[A <: R](x: F[A]): Unit
}
trait ModuleIntf {
type AbstractDomain
class Fnctr[X <: AbstractDomain]
}
// No mention of an actual concrete `Domain` up to
// this point. Now let's try to implement a concrete
// implementation of `ModuleIntf`:
sealed trait Domain
class Dom1 extends Domain
object ModuleImpl extends ModuleIntf {
type AbstractDomain = Domain
val c = new CoCone[Domain, Fnctr] { // error [1], error [2]
def apply[D <: Domain](x: Fnctr[D]): Unit = ()
}
}
everything breaks, and I get two error messages that I don't know how to interpret:
[1] error: kinds of the type arguments (Domain,Main.$anon.ModuleImpl.Fnctr) do not
conform to the expected kinds of the type parameters (type R,type F) in trait CoCone.
Main.$anon.ModuleImpl.Fnctr's type parameters do not match type F's expected parameters:
type X's bounds <: ModuleIntf.this.AbstractDomain are stricter than type _'s declared bounds <: R
val c = new CoCone[Domain, Fnctr] {
^
[2] error: kinds of the type arguments (Domain,Main.$anon.ModuleImpl.Fnctr) do not
conform to the expected kinds of the type parameters (type R,type F) in trait CoCone.
Main.$anon.ModuleImpl.Fnctr's type parameters do not match type F's expected parameters:
type X's bounds <: ModuleIntf.this.AbstractDomain are stricter than type _'s declared bounds <: R
val c = new CoCone[Domain, Fnctr] {
^
I expected that the compiler would recognize that inside ModuleImpl in CoCone[Domain, Fnctr] all three Domain = AbstractDomain = R are the same type.
Am I missing something obvious here, or is it a limitation of scalac 2.12.4 ? If it's a limitation, has someone ever reported it anywhere?
Edit Found something similar: issue #10186. Is it "the same"? Is not "the same"? Should I propose it as another test-case, if it is a bug? If someone can confirm that it's not entirely my fault, and/or that it's indeed closely related to the linked issue, that would be an acceptable resolution of the problem.
Edit2: As #Evgeny has pointed out, it cannot be exactly the issue 10186, because it fails in a different compiler phase (refchecks instead of typer).
Remark about the name: I've called the trait CoCone here, by analogy to the commonly defined ~> that can be thought of as a natural transformation, sort-of. In a way, the CoCone[Dom, Fctr] is something like Fctr ~> Const_Unit, but with domain of F restricted to subtypes of Dom. In reality, the CoCone[R, F] is a thing of shape F that can send certain subclasses of R over the network, but that's not important, so I've abstracted the names away. This thing is a rather common mathematical construction, nothing too contrived, would be nice if one could compile it.
Working approach with abstract type members (tried with scalac 2.12.4):
import scala.language.higherKinds
trait CoCone[R, F[_ <: R]] {
def apply[A <: R](x: F[A]): Unit
}
trait ModuleIntf {
type AbstractDomain
type X = ({type XX <: AbstractDomain; type XXX = XX with AbstractDomain})
class Fnctr[X]
}
sealed trait Domain
case class Dom1() extends Domain
object ModuleImpl extends ModuleIntf {
type AbstractDomain = Domain
val f = new Fnctr[Dom1]()
val c = new CoCone[Domain, Fnctr] {
def apply[X](x: Fnctr[X]): Unit = ()
}
c(f)
}
Idea is taken from comments for issue #4745. If I do not miss anything, this should be equivalent to original non-compilable approach.
As I found current problem compilation fails on different compiler phase (refchecks) when #10186 fails on typer, but anyway, in #10186 is mentioned patch, I tried it and it fixes #10186 itself, but current errors are still reported.
I would say that it should compile, but I did not find any issue similar to current problem, so, suppose, it is not yet reported compiler bug.
Updated after #Andrey comment.
Yes, was too focused to get compilable version and lost upper bound in trait. Sorry.
Updated after some more diving into compiler internals
I debug a bit validating higher kinded types (scala.reflect.internals.Kinds around checkKindBoundsHK) and looks like at the moment of checking Fnctr bounds, there are no info in bound types tree that AbstractDomain is alias for Domain. If I change CoCone first type to AbstractDomain in object, than anyway in tree I see that it is Domain, but not for Fnctr bounds.
By the way, fix for #10186 tries to solve something similar, evaluating bound argument asSeenFrom, as I understand trying to get concrete types, but as soon as in our case there is no into about concrete class in tree, AbstractDomain is returned..

dealing with extractors on case classes with an invariant type parameter

I think, my issue is best described by some sample code:
class Foo[T]
class Bar extends Foo[String]
class Baz extends Foo[Int]
trait X { def f: Foo[_] }
case class Wrapper[D](f: Foo[D]) extends X
val w: X = Wrapper(new Bar)
w match { case Wrapper(_: Bar) => 1 }
The last line fails with
found : Bar
required: Foo[Any]
Note: String <: Any (and Bar <: Foo[String]), but class Foo is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
I understand that this happens because unapply is defined with a type parameter, which is inferred as Any, and so it complains about String being incompatible.
But the question is if there is any way to make it work? I tried giving a type parameter to the exctractor, like this: w match { case Wrapper[String](_: Bar) => 1 }, but it says it does not take parameters (which is a lie) ... :(
The only way I came up with so far is this ugly baby:
w match { case w: Wrapper[String] if w.f.isInstanceOf[Bar] => 1 }
or, maybe,
Option(w).map(_.f) match { case Some(_: Bar) => 1 }
(the latter works because Option is covariant, but I can't make my classes covariant unfortunately). Also, I can't really use the last alternative without some additional ugliness IRL, because the equivalent of X in real life doesn't actually have f.
Any better ideas?
Define custom extractor
Wrapper.unapply does take type parameter, but you cannot specify one* in a pattern match sequence, so compiler infers one for you (and if compiler does that, it's very often Any or Nothing).
And, actually, you don't want it to, because you're removing your type information when you coerce your element to type X. So, you want a matcher for existential version
object WrapperEx {
def unapply(w: Wrapper[_]): Option[Foo[_]] = Wrapper.unapply(w)
}
And use it like so:
w match { case WrapperEx(_: Bar) => 1 }
Runnable version here
Good news: you can delegate to generated case class matcher.
Bad news: you cannot define it inside case class companion. Scala is happily picking the wrong one already, so it won't be able to disambiguate.
Still, I'd say it's not half-bad
* you can in latest Typelevel Scala, but I'm not sure how it works with type casts and I could not get it to work for your case.
You can parameterize trait X to get rid of existential type in def f: Foo[_], I think this is what trips the compiler up. The following code works:
class Foo[T]
class Bar extends Foo[String]
class Baz extends Foo[Int]
trait X[A] { def f: Foo[A] }
case class Wrapper[D](f: Foo[D]) extends X[D]
val w: X[String] = Wrapper(new Bar) // type ascription can be omitted and will be inferred
w match { case Wrapper(_: Bar) => 1 }

covariant type A occurs in contravariant position in type A of value a

I have following class:
case class Box[+A](value: A) {
def set(a: A): Box[A] = Box(a)
}
And the compiler complain:
Error:(4, 11) covariant type A occurs in contravariant position in type A of value a
def set(a: A): Box[A] = Box(a)
I was searching a lot about the error, but could not find something useful that
help me to understand the error.
Could someone please explain, why the error occurs?
The error message is actually very clear once you understand it. Let's get there together.
You are declaring class Box as covariant in its type parameter A. This means that for any type X extending A (i.e. X <: A), Box[X] can be seen as a Box[A].
To give a clear example, let's consider the Animal type:
sealed abstract class Animal
case class Cat extends Animal
case class Dog extends Animal
If you define Dog <: Animal and Cat <: Animal, then both Box[Dog] and Box[Cat] can be seen as Box[Animal] and you can e.g. create a single collection containing both types and preserve the Box[Animal] type.
Although this property can be very handy in some cases, it also imposes constraints on the operations you can make available on Box. This is why the compiler doesn't allow you to define def set.
If you allow defining
def set(a:A): Unit
then the following code is valid:
val catBox = new Box[Cat]
val animalBox: Box[Animal] = catBox // valid because `Cat <: Animal`
val dog = new Dog
animalBox.set(dog) // This is non-sensical!
The last line is obviously a problem because catBox will now contain a Dog! The arguments of a method appear in what is called "contravariant position", which is the opposite of covariance. Indeed, if you define Box[-A], then Cat <: Animal implies Box[Cat] >: Box[Animal] (Box[Cat] is a supertype of Box[Animal]). For our example, this is of course non-sensical.
One solution to your problem is to make the Box class immutable (i.e. to not provide any way to change the content of a Box), and instead use the apply method defined in your case class companion to create new boxes. If you need to, you can also define set locally and not expose it anywhere outside Box by declaring it as private[this]. The compiler will allow this because the private[this] guarantees that the last line of our faulty example will not compile since the set method is completely invisible outside of a specific instance of Box.
If for some reason you do not want to create new instances using the apply method, you can also define set as follows.
def set[B >: A](b: B): Box[B] = Box(b)
Others have already given an answer why the code doesn't compile, but they haven't given a solution on how to make the code compile:
> case class Box[+A](v: A) { def set[B >: A](a: B) = Box(a) }
defined class Box
> trait Animal; case class Cat() extends Animal
defined trait Animal
defined class Cat
> Box(Cat()).set(new Animal{})
res4: Box[Animal] = Box($anon$1#6588b715)
> Box[Cat](Cat()).set[Animal](new Animal{})
res5: Box[Animal] = Box($anon$1#1c30cb85)
The type argument B >: A is a lower bound that tells the compiler to infer a supertype if necessary. As one can see in the example, Animal is inferred when Cat is given.
Try to understand what it means for your Box[+A] to be covariant in A:
It means that a Box[Dog] should also be a Box[Animal], so any instance of Box[Dog] should have all the methods a Box[Animal] has.
In particular, a Box[Dog] should have a method
set(a: Animal): Box[Animal]
However, it only has a method
set(a: Dog): Box[Dog]
Now, you'd think you can infer the first one from the second, but that's not the case: I do you want to box a Cat using only the second signature? That's not doable, and that's what the compiler tells you: a parameter in a method is a contravariant position (you can only put contravariant (or invariant) type parameters).
in addition to the other answers i'd like to provide another approach:
def set[B >: A](x: B): Box[B] = Box(x)
Basically you cant put As in if A is covariant, you can only take it out (ex: returning A). If you wish to put As in, then you would need to make it contravariant.
case class Box[-A](value: A)
Do you want to do both, then just make it invariant
case class Box[A](value: A)
The best is to keep it covariant and get rid of the setter and go for an immutable approach.

Understanding the limits of Scala GADT support

The error in Test.test seems unjustified:
sealed trait A[-K, +V]
case class B[+V]() extends A[Option[Unit], V]
case class Test[U]() {
def test[V](t: A[Option[U], V]) = t match {
case B() => null // constructor cannot be instantiated to expected type; found : B[V] required: A[Option[U],?V1] where type ?V1 <: V (this is a GADT skolem)
}
def test2[V](t: A[Option[U], V]) = Test2.test2(t)
}
object Test2 {
def test2[U, V](t: A[Option[U], V]) = t match {
case B() => null // This works
}
}
There are a couple ways to make the error change, or go away:
If we remove the V parameter on trait A (and case class B), the 'GADT-skolem' part of the error goes away but the 'constructor cannot be instantiated' part remains.
If we move the U parameter of the Test class to the Test.test method, the error goes away. Why ? (Similarly, the error is not present in Test2.test2)
The following link also identifies that problem, but I do not understand the provided explanation. http://lambdalog.seanseefried.com/tags/GADTs.html
Is this an error in the compiler ? (2.10.2-RC2)
Thank you for any help with clarifying that.
2014/08/05: I have managed to further simplify the code, and provide another example where U is bound outside the immediate function without causing a compilation error. I still observe this error in 2.11.2.
sealed trait A[U]
case class B() extends A[Unit]
case class Test[U]() {
def test(t: A[U]) = t match {
case B() => ??? // constructor cannot be instantiated to expected type; found : B required: A[U]
}
}
object Test2 {
def test2[U](t: A[U]) = t match {
case B() => ??? // This works
}
def test3[U] = {
def test(t: A[U]) = t match {
case B() => ??? // This works
}
}
}
Simplified like that this looks more like a compiler bug or limitation. Or am I missing something ?
Constructor patterns must conform to the expected type of the pattern, which means B <: A[U], a claim which is true if U is a type parameter of the method presently being called (because it can be instantiated to the appropriate type argument) but untrue if U is a previously bound class type parameter.
You can certainly question the value of the "must conform" rule. I know I have. We generally evade this error by upcasting the scrutinee until the constructor pattern conforms. Specifically,
// Instead of this
def test1(t: A[U]) = t match { case B() => ??? }
// Write this
def test2(t: A[U]) = (t: A[_]) match { case B() => ??? }
Addendum: in a comment the questioner says "The question is simple on why it doesn't works with type parameter at class level but works with at method level." Instantiated class type parameters are visible externally and have an indefinite lifetime, neither of which is true for instantiated method type parameters. This has implications for type soundness. Given Foo[A], once I'm in possession of a Foo[Int] then A must be Int when referring to that instance, always and forever.
In principle you could treat class type parameters similarly inside a constructor call, because the type parameter is still unbound and can be inferred opportunistically. That's it, though. Once it's out there in the world, there's no room to renegotiate.
One more addendum: I see people doing this a lot, taking as a premise that the compiler is a paragon of correctness and all that's left for us to do is ponder its output until our understanding has evolved far enough to match it. Such mental gymnastics have taken place under that tent, we could staff Cirque de Soleil a couple times over.
scala> case class Foo[A](f: A => A)
defined class Foo
scala> def fail(foo: Any, x: Any) = foo match { case Foo(f) => f(x) }
fail: (foo: Any, x: Any)Any
scala> fail(Foo[String](x => x), 5)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at $anonfun$1.apply(<console>:15)
at .fail(<console>:13)
... 33 elided
That's the current version of scala - this is still what it does. No warnings. So maybe ask yourselves whether it's wise to be offering the presumption of correctness to a language and implementation which are so trivially unsound after more than ten years of existence.
Looks like it is a compiler caveat. From this, martin odersky puts it as:
In the method case, what you have here is a GADT:
Patterns determine the type parameters of an corresponding methods in the scope of a pattern case.
GADTs are not available for class parameters.
As far as I know, nobody has yet explored this combination, and it looks like it would be quite tricky to get this right.
PS: Thanks to #retronym who provided the reference from discussion here
On why it throws an error in case of class:
This works:
sealed trait A[-K, +V]
case class B[+V]() extends A[Option[Unit], V]
case class Test[U]() {
def test[V, X <: Unit](t: A[Option[X], V]) = t match {
case B() => null
}
def test2[V](t: A[Option[U], V]) = Test2.test2(t)
}
object Test2 {
def test2[U, V](t: A[Option[U], V]) = t match {
case B() => null // This works
}
}
To examplain why the compiler threw an error: Try doing this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
abstract class Exp[A]{
def eval:A = this match {
case Temp(i) => i //line-a
}
}
case class Temp[A](i:A) extends Exp[A]
// Exiting paste mode, now interpreting.
To understand this, from §8.1.6: above Temp is a polymorphic type. If the case class is polymorphic then:
If the case class is polymorphic, then its type parameters are
instantiated so that the instantiation of c conforms to the expected
type of the pattern. The instantiated formal parameter types of c’s
primary constructor are then taken as the expected types of the
component patterns p 1 , . . . , p n . The pattern matches all objects
created from constructor invocations c(v1 , . . . , v n ) where each
element pattern p i matches the corresponding value v i .
i.e. the compiler smartly tries to instantiate Temp in line-a such that it conforms to the primary constructor of this( which if above compiled successfully then in case of Temp(1) would be something like Exp[Int] and hence compiler instantiates Temp in line-a with parameter as Int.
Now in our case: the compiler is trying to instantiate B. It sees that t is of type A[Option[U],V] where U is already fixed and obtained from class parameter and V is generic type of method. On trying to initialize B it tries to create in such a way that it ultimately gets A[Option[U],V]. So with B() it is somehow trying to get A[Option[U],V]. But it cant as B is A[Option[Unit],V]. Hence it ultimately cannot initialize B. Fixing this makes it work
Its not required in case of test-2 because: The compiler as explained in above process is trying to initialize type parameter of B. It knows t has type parameter [Option[U], V] where U and V are both generic wrt method and are obtained from argument. It tries to initialize B based on the agrument. If the argument was new B[String] it tries deriving B[String] and hence U is automatically obtained as Option[Unit]. If the argument was new A[Option[Int],String] then it obviously it wont match.
The difference has to do with what information the compiler and runtime have in each case, combined with what the restrictions on the types are.
Below the ambiguity is clarified by having U be the trait and class parameter, and X be the method type paramter.
sealed trait A[U]
case class B(u: Unit) extends A[Unit]
class Test[U]() {
def test(t: A[U]) = t match {
case B(u) => u // constructor cannot be instantiated to expected type; found : B required: A[U]
}
}
object Test2 {
def test2[X](t: A[X]) = t match {
case B(x) => x // This works
}
def test3[X] = {
def test(t: A[X]) = t match {
case B(x) => x // This works
}
}
}
Test2.test2(new B(println("yo")))
In Test2.test2, the compiler knows that an instance of A[X] will be provided, with no limits on X.It can generate code that inspects the parameter provided to see if it is B, and handle the case.
In class Test method test, the compiler knows not that some A[X] is provided when called, but that some specific type, U, is presented. This type U can be anything. However, the pattern match is on an algebraic data type. This data type has exactly one valid variation, of type A[Unit]. But this signature is for A[U] where U is not limited do Unit. This is a contradiction. The class definition says that U is a free type parameter, the method says it is Unit.
Imagine you instantiate Test:
val t = new Test[Int]()
Now, at the use site, the method is:
def test(t: A[Int])
There is no such type A[Int]. Pattern matching on B is when the compiler inspects this condition. A[U] for any U is incompatible with the type, hence "constructor cannot be instantiated to expected type; found : B required: A[U]"
The difference with the method versus class type parameter is that one is bound while one is free, at the time the method is called in the runtime.
Another way to think about it is that defining the method
def test(t: A[U])
implies that U is Unit, which is inconsistent with the class declaration that U is anything.
Edit: this is not an answer to the question. I'm keeping it for reference (and the comments).
The link you provided already gives the answer.
In the case of the parameterised method, U is infered from the argument of the actual method call. So, the fact that the case B() was chosen implies that U >: Unit (otherwise the method could not have been called with a B) and the compiler is happy.
In the case of the parameterized class, U is independent from the method argument. So, the fact that the case B() was chosen tells the compiler nothing about U and it can not confirm that U >: Unit. I think if you add such a type bound to U it should work. I haven't tried it, though.
The compiler is absolutely correct with its error message. An easy example should describe the issue well:
sealed trait A[U]
class B extends A[Unit]
class T[U] {
val b: A[Unit] = new B
f(b) // invalid, the compiler can't know which types are valid for `U`
g(b) // valid, the compiler can choose an arbitrary type for `V`
def f(t: A[U]) = g(t)
def g[V](t: A[V]) = ???
}
f and g have different type signatures. g takes an A[V] where V is an arbitrary type parameter. On the other hand, f takes an A[U] where U is not arbitrary but dependent from the outer class T.
The compiler does not know what type U can be at the moment when it typechecks T - it needs to typecheck an instantiation of T to find out which types are used. One can say that U is a concrete type inside of T, but a generic type outside of it. This means it has to reject every code fragment that gets inside of T more concrete about the type of U.
Calling g from inside f is clearly allowed - after all an arbitrary type for V can be chosen, which is U in this case. Because U is never again referenced in g it doesn't matter what type it may have.
Your other code example underlies the same limitations - it is just an example that is more complex.
Btw, that this code
def test3[U] = {
def test(t: A[U]) = t match {
case B() => ??? // This works
}
}
is valid looks weird to me. It doesn't matter if U is bound by a class or a method - from inside of the binding scope we can't give any guarantees about its type, therefore the compiler should reject this pattern match too.