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.
scalac -Xprint:typer,refchecks -Ydebug -Xprint-types -Ytyper-debug test3.scala &> test3.scalac
where test3.scala contains your case. Same keys (without refchecks, because it stopped on typer) for issue's code gave me stop with error on typer. – Evgenyscalac -Xprint:all
shows that it fails duringrefchecks
. I think I'll wait till next Tuesday-Wednesday, see if someone knows more... If you are reasonably certain that this has not been reported, I wouldn't mind if you convert comment into answer. – Andrey Tyukin-explaintypes
could add some output on error, but it does not give me anything to understand.. I tried move param which parameterizes Fnctr to trait (trait ModuleIntf[AbstractDomain, X <: AbstractDomain]
) but finished withclass ModuleImpl[X]
whereX
is also parameter forapply
. This compiles, but again, does not give me understanding in original problem.. – Evgeny