3

I want to implement the enrich-my-library pattern for the inner class that will work for any instance of the outer class. Something like this:

class Outer {
  class Inner(val x: Option[Inner] = None) {
    def test(y: Inner) {}
  }
}

implicit class ExtInner(inner: Outer#Inner) {
  def get = inner.x.get
}

val outer = new Outer
val inner = new outer.Inner(Some(new outer.Inner))
inner.test(inner.get)

This code will not compile, because of the type mismatch: get returns an object of type Outer#Inner but test expects outer.Inner.

I have two ways to make it work but both involve the use of asInstanceOf which I would like to avoid. The first one is simply to cast the result of get to outer.Inner:

inner.test(inner.get.asInstanceOf[outer.Inner]) 

The second is a bit more generic and does the casting in ExtInner:

implicit class ExtInner[T <: Outer#Inner](inner: T) {
  def get = inner.x.get.asInstanceOf[T]
}

Is there a better way to enrich an inner class so there will be no need to do the casting?

0__
  • 66,707
  • 21
  • 171
  • 266
gleb
  • 143
  • 7

1 Answers1

1

How about:

object Test extends App {
  class Outer {
    class Inner(val x: Option[Inner] = None) {
      def test(y: Inner) { println(s"$toString testing $y") }
    }
    implicit class ExtInner(val inner: Inner) {
      def get = inner.x.get
    }
  }

  val outer = new Outer
  val inner = new outer.Inner(Some(new outer.Inner))
  import outer._
  inner.test(inner.get)
}

or just enhance outer's Inners:

  implicit class ExtInner(val inner: outer.Inner) {
    def get = inner.x.get
  }

Also, I sense that you can invoke the method this way, but it will not infer the singleton type param outer.type in order to summon the implicit.

  implicit class ExtInner[A <: Outer with Singleton](val inner: A#Inner) {
    def get: A#Inner = inner.x.get
  }
  inner.test(ExtInner[outer.type](inner).get)
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Pimp my library pattern assumes that the `Outer` class can't be modified so taking the `ExtInner` inside the `Outer` class is not really a solution. Having `ExtInner` accept `outer.Inner` instead of `Outer#Inner` binds it to the concrete instance, which is undesirable because we will need to define a new implicit conversion for each instance of the `Outer` class. – gleb Jun 07 '13 at 14:35
  • Then you have to accept my third answer that you can't get the implicit applied. It's analogous to the regex conversion in the Parsers mini-cake, so it must be a common use case. There's always reflection and macros. – som-snytt Jun 07 '13 at 15:16
  • Your third answer does the job without casting although it requires a bit of boilerplate code. Can you comment on why we need `with Singleton`, it seems to work without it as well? – gleb Jun 09 '13 at 10:12