Take the 2-minute tour ×

Let's say a I have a class:

case class Foo(id: Int, name: String, note: Option[String] = None)

Both the constructor and the apply method in the automatically generated companion object take three parameters. When viewed via reflection, the third parameter (note) is flagged:

p.isParamWithDefault = true

Also, by inspection I can find the method that produces the value in the companion object:

method <init>$default$3


method apply$default$3

Which both also have:

m.isParamWithDefault = true

However, I can neither find anything on the TermSymbol for the notes parameter that actually points me at the right methods to obtain the default value nor anything on the above MethodSymbols that point back to the TermSymbol for the parameter.

Is there a straight forward way to link TermSymbol for the parameter with the method that generates its default value? Or do I need to do something kludgey like inspect the names of the methods on the companion object?

I'm interested in this both for the case class constructor example I have here and for regular methods.

share|improve this question
There are degrees of kludge. Sample code at [this answer.][1] [1]: stackoverflow.com/a/13813000/1296806 –  som-snytt Dec 25 '12 at 23:45
Yeah, I've coded something similar to that. But it relies on Scala's name mangling scheme which should be treated as an implementation detail. In fact there a thread on it going on right now: groups.google.com/d/topic/scala-internals/aE81MVdIhCk/… –  Erik Engbrecht Dec 25 '12 at 23:51
Yes, also following the thread; my interest stemmed from a PR that needed to go from default args to the method, etc, it's messy interally, let alone externally. But as noted, the form of the mangle is spec'd. –  som-snytt Dec 25 '12 at 23:58
If I understand correctly name mangling for default argument methods is specified in the SID for named and default arguments but it is not in the SLS. I'm not sure, but I don't believe SIDs are treated as hard specifications to which the implementation must conform. They're more general design documents. –  Erik Engbrecht Dec 26 '12 at 0:35
My answer edit includes both SLS locs. You'd think it would be impl detail. My use case is, I make a dumb exception class with an optional message, I'd hate to generate a companion just for the default arg. Alternative would be to overload the constructor, as you'd normally do by hand. –  som-snytt Dec 26 '12 at 1:43
add comment

2 Answers

up vote 5 down vote accepted

There are degrees of kludge.

Sample code at this answer, pasted below.

So as I was saying, the form of the name is in the spec at 4.6, 6.6.1. That is not ad-hoc. For every parameter pi , j with a default argument a method named f $default$n is generated which computes the default argument expression.

The lack of structured ability to access and reconstitute these generated names is a known issue (with a current thread on the ML).

import reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._

// case class instance with default args

// Persons entering this site must be 18 or older, so assume that
case class Person(name: String, age: Int = 18) {
  require(age >= 18)

object Test extends App {

  // Person may have some default args, or not.
  // normally, must Person(name = "Guy")
  // we will Person(null, 18)
  def newCase[A]()(implicit t: ClassTag[A]): A = {
    val claas = cm classSymbol t.runtimeClass
    val modul = claas.companionSymbol.asModule
    val im = cm reflect (cm reflectModule modul).instance
    defaut[A](im, "apply")

  def defaut[A](im: InstanceMirror, name: String): A = {
    val at = newTermName(name)
    val ts = im.symbol.typeSignature
    val method = (ts member at).asMethod

    // either defarg or default val for type of p
    def valueFor(p: Symbol, i: Int): Any = {
      val defarg = ts member newTermName(s"$name$$default$$${i+1}")
      if (defarg != NoSymbol) {
        println(s"default $defarg")
        (im reflectMethod defarg.asMethod)()
      } else {
        println(s"def val for $p")
        p.typeSignature match {
          case t if t == typeOf[String] => null
          case t if t == typeOf[Int]    => 0
          case x                        => throw new IllegalArgumentException(x.toString)
    val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
    (im reflectMethod method)(args: _*).asInstanceOf[A]

  assert(Person(name = null) == newCase[Person]())
share|improve this answer
Thanks for adding the references to the SLS. When I had checked it I had missed it. I was hoping for something cleaner, but at least the appears to rely on public interfaces and specified conventions. –  Erik Engbrecht Dec 26 '12 at 2:03
We also have issues.scala-lang.org/browse/SI-6468 –  Eugene Burmako Dec 26 '12 at 9:47
add comment

You can do this without making assumptions about the generated names—by casting to the internal API:

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

defined class Foo

scala> val t = typeOf[Foo.type]
t: $r.intp.global.Type = Foo.type

scala> t.declaration(nme.defaultGetterName(nme.CONSTRUCTOR, 3))
res0: $r.intp.global.Symbol = method <init>$default$3

scala> t.declaration(nme.defaultGetterName(newTermName("apply"), 3))
res1: $r.intp.global.Symbol = method apply$default$3

Of course in a way this isn't any nicer, since the name mangling is specified and the internal API isn't, but it may be more convenient.

share|improve this answer
Fixing the encoding for constructor was my doing. Note the anomaly: scala> nme.defaultGetterName(nme.MIXIN_CONSTRUCTOR, 3) is res0: $r.intp.global.TermName = $lessinit$greater$default$3. By anomaly, I mean bug if it mattered. So arguably, it's better to use the implementation-dependent, anomaly-preserving API. –  som-snytt Dec 26 '12 at 0:34
add comment

Your Answer


