d.winmain
d.winmain

Reputation: 35

Can I "Pimp my Library" on a parameterized trait with method returning this.type?

I want to extend my parameterized trait Field[T] with method returning this.type using Pimp my Library pattern. But I stuck with compiler errors. I tried some variants, but without success.

Am I doing it wrong? Or asking for the impossible?

Here is my test example:

trait Field[T]

class IntField extends Field[Int] {
  def getInt = 5
}

// Variant 1: return field.type
object Var1 {
  implicit class RichField[T](val field: Field[T]) {
    def bar(fn: T => String): field.type = {
      // some actions using T
      field
    }
  }

  new IntField().bar(_.toString).getInt  // Error: value getInt is not a member of Field[Int]
}

// Variant 2: use two type parameters
object Var2 {
  implicit class RichField[T, F <: Field[T]](val field: F) {
    def bar(fn: T => String): F = {
      // some actions using T
      field
    }
  }

  new IntField().bar(_.toString).getInt // <-- Error: value bar is not a member of IntField
}

// Variant 3: use higher kinds
object Var3 {
  import scala.language.higherKinds

  implicit class RichField[F[X] <: Field[X], T](val field: F[T]) {
    def bar(fn: T => String): F[T] = {
      // some actions using T
      field
    }
  }

  new IntField().bar(_.toString).getInt // <-- Error: value getInt is not a member of Field[Int]
}

Update: Parameter T is important for method bar, it cannot be ignored.

Upvotes: 1

Views: 248

Answers (1)

R&#233;gis Jean-Gilles
R&#233;gis Jean-Gilles

Reputation: 32719

In variant 2, you are not using T anywhere. Just remove it and the compiler won't be confused anymore:

implicit class RichField[F <: Field[_]](val field: F) {
  def bar: F = field
}

UPDATE: If you actually need T, as you mentioned in a comment, a possible workaround is this:

implicit class RichField[T, F <: Field[_]](val field: F with Field[T]) {
  def bar(fn: T => String): F = {
    // some actions using T
    field
  }
}

And here's a proof that it works as expected (using toString as in your example is not a good test given that toString is available on every class, so even if the compiler inferred Any the code would have compiled):

scala> case class Foo(name: String)
defined class Foo

scala> class FooField extends Field[Foo] {
     |   def getInt = 5
     | }
defined class FooField

scala> new FooField().bar(_.name).getInt
res8: Int = 5

Upvotes: 2

Related Questions