Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - how to create anonymous class and avoid hiding argument names

Tags:

scala

I often encounter the situation where I have a factory method for some trait and the names of the arguments clash with the members of the trait causing them to be hidden:

trait MyTrait {
    val a: Int
    val b: String
}

object MyTrait {
    def apply(a: Int, b: String): MyTrait = new MyTrait {
        val a = a // Recursive infinite loop.
        val b = b
    }
}

So usually I have to do something ugly like:

    def apply(aA: Int, bB: String): MyTrait = new MyTrait {
      val a = aA
      val b = bB
    }

or make local copies of the arguments:

    def apply(a: Int, b: String): MyTrait = {
      val localA = a
      val localB = b

      new MyTrait {          
          val a = localA
          val b = localB
    }

I would like the parameters to the apply method to be the same as the members of my trait, so that client code reads nicely when I used named parameters: e.g. MyTrait(a=3,b="123").

Is there a neater mechanism that lets me capture the outer scope where the argument parameters are defined, but the anonymous class isn't yet? For example something like:

    def apply(a: Int, b: String): MyTrait = { outer =>
        new MyTrait {
            val a = outer.a
            val b = outer.b
        }
    }

Thanks!

like image 744
rmin Avatar asked Sep 04 '14 00:09

rmin


2 Answers

If we need to create an instance, why not delegate it to the constructor. So I can think of this hack:

trait MyTrait {
  def a: Int
  def b: String
}

object MyTrait {
  private class MkMyTrait(val a: Int, val b: String) extends MyTrait
  def apply(a: Int, b: String): MyTrait = new MkMyTrait(a, b)
}
like image 73
4e6 Avatar answered Nov 02 '22 06:11

4e6


My answer to the dupe, which doesn't save much:

trait T { val t: Int }
object T {
  def apply(t: Int): T = new {
    private[this] val x = t
  } with T {
    val t = x
  }
}

Also, -optimise doesn't get rid of the field.

Here's a different idea: alias the param name.

Soon, we'll be able to filter warnings selectively, so it will be possible to ignore the warning that is generated when deprecation is enabled:

scala> :pa
// Entering paste mode (ctrl-D to finish)

trait T { val t: Int }
object T {
  def apply(@deprecatedName('t) t0: Int): T = new T { val t = t0 }
}

// Exiting paste mode, now interpreting.

defined trait T
defined object T

scala> T(t = 42)
warning: there was one deprecation warning; re-run with -deprecation for details
res1: T = T$$anon$1@6ea1bcdc
like image 2
som-snytt Avatar answered Nov 02 '22 05:11

som-snytt