What does $$[T] mean in method name

106 views Asked by At

I recently came across a odd syntax in Scala that I've never seen before and there seems to be no mention of it anywhere in Scala docs. I have a suspicion that this is just an odd way to name a method, but wanted to check if it's not something else.

class Reader(props: PropertyResolver, prefix: Option[String] = None {
   def $$[T](name: String)(implicit conv: String => T): T => props.$[T](prefix.fold(name)(_ + name))
}

Just a general wondering whether I understand correctly that somebody was crazy enough to call method $$

1

There are 1 answers

2
Andrey Tyukin On

Why it works

Despite violating naming conventions, $$ is a valid method identifier. It's ordinary even by JVM's standards, i.e. it does not require any Scala-specific name mangling (the dollar sign $ is a valid identifier name in Java). This is all valid Java (bad, but valid):

// Compiles just fine when saved in `$.java`.
class $ {

  private String _$;

  public $(String __) {
    _$ = __;
  }

  private void _$_() {
    System.out.println(_$);
  }

  private static void $$($ ___) {
    ___._$_();
  }

  public static void main(String... ____) {
    $$(new $("Dude, why..."));
  }
}

It's in no way specific to the JVM either. JavaScript's jQuery has been doing this for decades. This is valid JS code:

function $(selector) { return { click(){} } }

and allows you to do these typical jQuery-style-selections, as in

$('#header').click((ev) => {
  /* ... */
})

Why you shouldn't do it

While the JVM doesn't care and works with $s in identifiers just fine, in the actual programming languages (Scala, Java), the usage of $ in identifiers is strongly discouraged, because your identifiers might inadvertently collide with synthetic identifiers generated by the compilers. For example, this Java program:

// javac Dollarz.java
class Dollarz {
  static class A {}
}

class Dollarz$A {}

does not actually compile, because our manually defined Dollarz$A class collides with the synthetic name that the javac generates for the nested class A.

Similarly, for Scala, the following code snippet:

// Try `scalac Dollarz.scala`
class Dollarz$
object Dollarz

produces a strange error message Dollarz is already defined as class Dollarz in Dollarz.scala, despite class Dollarz$ and object Dollarz compiling just fine on their own. The problem this time is that scalac tries to generate a synthetic class Dollarz$ for the companion object Dollarz, which then ends up colliding with the already defined class Dollarz$.

Moreover: even if your code with $-characters in identifiers compiles now, the backward compatibility is not guaranteed, i.e. you should treat all names with $s as reserved by the compiler designers. If in Scala 3.45 someone decides to generate bunch of synthetic methods $$, $$1, $$2 ... on every class, and your code breaks because of the collisions, then it will be your fault. The Spec explicitly tells you not to use it in user programs:

The ‘$’ character is reserved for compiler-synthesized identifiers. User programs should not define identifiers that contain ‘$’ characters.