Given a polymorphic type Distribution, I want to establish, what I would call a typeclass on this (polymorphic) type. I think this goes under the term Higer Kinded Types.
trait Distribution[A] extends Measure[A] with Random[A] {
type Domain = A
}
Distributions instantiating this typeclass should implement a method train, which given a List of elements in the domain of the distribution, return an object this distribution.
trait ML[D <: Distribution[_] ] {
def train( samples: List[D#Domain] ): D
}
But when I try to instantiate like this (Binomial is a Distribution[Int]):
implicit object MLBinomial extends ML[Binomial] {
def train( samples: List[Int] ) : Binomial = ???
}
I get the following error:
Binomial.scala:181: object creation impossible, since method train in trait ML
of type (samples: List[_$1])org.lanyard.dist.disc.Binomial is not defined
(Note that List[_$1] does not match List[Int]: their type parameters differ)
I am guessing I am the victim of type erasure but I am not sure and just learning to fight with the scala type system. Is anybody able to fix this code or am I forced to use a different design?
Thank you in advance.
When you're defining
trait ML[D <: Distribution[_]] {
def train(samples: List[D#Domain]): D
}
then D#Domain
cannot be fixed because you're saying that D
is a subtype of Distribution[_]
where you really don't care about what _
actually is. If instead you want to infer the inner type, you could do as follows:
trait Measure[T]
trait Random[T]
trait Distribution[A] extends Measure[A] with Random[A] {
type Domain = A
}
trait Binomial extends Distribution[Int]
trait ML[A, D <: Distribution[A]] {
def train(samples: List[A]): D // could be List[D#Domain] now as well
}
object ML {
implicit object MLBinomial extends ML[Int, Binomial] {
def train(samples: List[Int]) : Binomial = ???
}
}
EDIT: If you don't want to mess around with multiple type parameters, you could also restrict Domain
to a subtype of A
. This will type-check and (hopefully) give you what you want. You'd only have to specify Domain
when defining Binomial
:
trait Distribution[A] extends Measure[A] with Random[A] {
type Domain <: A
}
trait Binomial extends Distribution[Int] { type Domain = Int }
trait ML[D <: Distribution[_]] {
def train(samples: List[D#Domain]): D
}
object ML {
implicit object MLBinomial extends ML[Binomial] {
def train(samples: List[Int]): Binomial = ???
}
}