Home > Blockchain >  Generalised type constraints and this
Generalised type constraints and this

Time:08-18

I'm trying to add a method to a generic class that should only work for some types, and I understand that a generalised type constraint is a way to achieve this. But I'm struggling to use this when the instance itself (this) needs to be passed, like so:

case class Sum(x: Value[Int], y: Value[Int]) {
  def apply(): Int = x.v   y.v
}

case class Value[T](v: T) {
  def plus(y: Value[Int])(using ev: T =:= Int): Int = Sum(this, y)()
}

I would have thought that the T =:= Int would have given the compiler enough information to know that this is a Value[Int], but it complains:

-- [E007] Type Mismatch Error: -------------------------------------------------
6 |  def plus(y: Value[Int])(using ev: T =:= Int): Int = Sum(this, y)()
  |                                                          ^^^^
  |                                         Found:    (Value.this : Value[T])
  |                                         Required: Value[Int]

Is there a way to do what I am wanting here and have the compiler know that this is, indeed, Value[Int]?

Thanks for any help! ✌️

CodePudding user response:

Unfortunately, no, passing T =:= Int, is not enough.

The way =:= work is that if at some place compiler sees that implicit/given of T =:= T is required, it provides it.

At the callsite of value1.plus(value2) compiler can see that and create this evicence.

Once it is obtained, here: inside plus, =:= is NOT becoming a magic hint for the compiler for every type. It becomes implicit conversion. If you'd do:

case class Value[T](v: T) {
  def plus(y: Value[Int])(using ev: Value[T] =:= Value[Int]): Int = Sum(this, y)()
}

you'd see that it works, but with the help of e.g. IntelliJ "show implicits" hint option you'd see that compiler turns your code into

case class Value[T](v: T) {
  def plus(y: Value[Int])(using ev: Value[T] =:= Value[Int]): Int = Sum(ev(this), y)()
}

This implicit conversion would out of the box work only on matching types.

However, there are other options:

case class Value[T](v: T) {
  def plus(y: Value[Int])(using ev: T =:= Int): Int = Sum(ev.subsctituteCo[Value](this), y)()
}

ev.subsctituteCo let you add a wrapper type and cast wrapped value from the type on the left to the type on the right.

You could also cast it yourself with .asInstanceOf (less elegant) or unwrap and rewrap value like suggested in the comments.

  • Related