The problem is simple, I have an object which does a binary operation with two parameters. I want to only add fuels that have the same type, like this:
object Fuels {
case class Fuel[F <: FuelType](amount: Double, fuelType: F = Petrol) {
def +(that : Fuel[F]) = {
copy(amount = this.amount + that.amount)
}
}
def add[F <: FuelType](x: Fuel[F], y: Fuel[F]): Fuel[F] = x + y
sealed trait FuelType {
val name : String
}
case object Petrol extends FuelType{
override val name = "Petrol"
}
case object Diesel extends FuelType{
override val name = "Diesel"
}
case object Hydrogen extends FuelType{
override val name = "Hydrogen"
}
implicit def fuelMonoid[F <:FuelType](implicit fuelType: F) = new Monoid[Fuel]{
override def zero: Fuel[F] = Fuel(0, fuelType)
override def append(m1: Fuel[F], m2: Fuel[F]) : Fuel[F] = m1 + m2
}
}
Use it:
> Fuel(10, Petrol) + Fuel(20, Petrol)
> add(Fuel(10, Petrol), Fuel(10, Petrol))
Compilation error:
Expression of Type Fuels.Fuel[Nothing] does not conform to Fuels.Fuel[F]
The main issue is that both add and fuelMonoid fail to identify that we are dealing with items of the same type. The compiler can't resolve the type constraint, and infers Nothing.
For completion, here's Monoid, nothing extraordinary:
trait Monoid[A] {
def zero: A
def append(a1: A, a2: A): A
}