Search code examples
scalareflectionscala-reflect

Comparing Scala reflection Symbols


The Types Scaladoc page warns:

Type Equality can be checked with =:=. It's important to note that == should not be used to compare types for equality-- == can't check for type equality in the presence of type aliases, while =:= can.

There is no similar warning for Symbols, but looking at the implementation, it doesn't appear to override equals. Is there a way to compare symbols for equality (i.e. whether they represent the same Scala type/val/method/etc.)?

For TypeSymbols I can obviously use .toType and =:=, so the question is primarily about TermSymbols.


Solution

  • It seems as though == is fine for symbols (to an extent). I don't want to try to over-interpret the scaladoc, but I don't think aliases matter with them. (I would also hope that the section on symbols would contain a similar warning.)

    Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method. Anything you define and can give a name to in Scala has an associated symbol.

    versus Type:

    As its name suggests, instances of Type represent information about the type of a corresponding symbol. This includes its members (methods, fields, type aliases, abstract types, nested classes, traits, etc.) either declared directly or inherited, its base types, its erasure and so on. Types also provide operations to test for type conformance or equivalence.

    The documentation suggests that Types contain more valuable information than Symbols.

    Example:

    type L[A] = List[A]
    
    scala> typeOf[L[String]].typeSymbol == typeOf[List[String]].typeSymbol
    res47: Boolean = true
    

    The Symbols are equal, even though the Types are not. So while L[A] and List[A] have different Types from the alias, they both resolve to the same Symbol. The inner-type information appears to be gone though, and the Symbol appears to contain the information on the List class itself.

    scala> typeOf[List[String]].typeSymbol
    res51: reflect.runtime.universe.Symbol = class List
    
    scala> typeOf[L[String]].typeSymbol
    res52: reflect.runtime.universe.Symbol = class List
    

    So these are equal:

    scala> typeOf[L[String]].typeSymbol == typeOf[L[Int]].typeSymbol
    res55: Boolean = true
    

    While these are not:

    scala> typeOf[L[String]] =:= typeOf[L[Int]]
    res56: Boolean = false
    

    So while it appears while the underlying types should have the same Symbol, the Symbol might not contain all the information you need for the full comparison illustrated above.