Scala, cannot avoid cyclic parameterization
I have reduced my problem to MWE (minimal (not) working example). Here it is:
First, we have a datatype Loss
which is essentially a Function2
and therefore contravariant in T.
abstract class Loss[-T] {
def apply(actual: T, predicted: T): Double
}
Then we have a label whose loss is a property:
abstract class Labeled[+T <: Labeled[T]] {
def loss[Q >: T]: Loss[Q]
}
It is also naturally covariant and must be F-restricted. Up to this point, the program is compiled. The problem arises when I want to make a concrete implementation of the case class of an abstract class Labeled
. I've tried this:
Error:(9, 12) class Numerical needs to be abstract, since method loss in class Labeled of type [Q >: Numerical]=> Loss[Q] is not defined
case class Numerical(name: Symbol, loss: Loss[_ >: Numerical]) extends Labeled[Numerical] {
^
However, I try to parameterize it, I get a circular definition. How can this be done, or if not, tell me what I think is wrong.
How I got it working (warning: bad hack): I removed the abstract spec Loss
from Labeled
and just kept the above definition across all subclasses. When I need to call Loss
on a generic class Labeled
, I match templates for every possible subclass. Hope it gets resolved someday.
source to share
As said in the comments, you cannot override a parameterized method with a value, so you probably have to move Q
from the definition loss
:
abstract class Labeled[+T <: Labeled[T]] {
type TT <: T
type Q >: TT
def loss: Loss[Q]
}
case class Numerical(name: Symbol, loss: Loss[Numerical#Q]) extends Labeled[Numerical]
You can specify Q yourself (the existential type _ >: Numerical
presented Q >: TT
here will resolve to Any
otherwise):
case class Numerical(name: Symbol, loss: Loss[Numerical#Q]) extends Labeled[Numerical] {
type Q = Numerical //can only be >: than Numerical
}
So now you can use it:
scala> :paste
// Entering paste mode (ctrl-D to finish)
Numerical('a, new Loss[Numerical#Q] {
def apply(actual: Numerical#Q, predicted: Numerical#Q): Double = {
actual.loss //check that it available, as you put `Q = Numeric`
0L
}
})
// Exiting paste mode, now interpreting.
res19: Numerical = Numerical('a,$anon$1@6355264c)
source to share