Search code examples
javascalagenerics

Why do these functions have the same type after erasure?


def merge[X](list1: Option[List[X]], list2: Option[List[X]]): Option[List[X]]  
def merge[X](list1: Option[List[X]], elem: Option[X]): Option[List[X]]

The compiler says that these two functions have same type after erasure

def merge[X](list1: Option[List[X]],list2: Option[List[X]]): Option[List[X]] at line 122 and
def merge[X](list1: Option[List[X]],elem: Option[X]): Option[List[X]] at line 131
have same type after erasure: (list1: Option, list2: Option)Option

Why does the Option[List[X]] and Option[X] become same after erasure? How can I make them different?


Solution

  • Why does the Option[List[X]] and Option[X] become same after erasure?

    Because the generic type parameter of Option[...] is erased (so both X and List[X] are erased) so both methods will have the same signature.

    How can I make them different?

    One approach - adding dummy implicit parameter to one of the methods so they have different signatures:

    def merge[X](list1: Option[List[X]], list2: Option[List[X]]): String = "1"
    def merge[X](list1: Option[List[X]], elem: Option[X])(implicit d: DummyImplicit): String = "2"
    
    println(merge(Some(List(1)), Some(List(2)))) // 1
    
    println(merge(Some(List(1)), Some(2))) // 2
    

    For Scala 3 (thanks to @Gaël J for pointing that out) @targetName annotation can be used to resolve the ambiguity:

    import scala.annotation.targetName
    def merge[X](list1: Option[List[X]], list2: Option[List[X]]): String = "1"
    @targetName("merge_element")
    def merge[X](list1: Option[List[X]], elem: Option[X]): String = "2"
    
    println(merge(Some(List(1)), Some(List(2)))) // 1
    println(merge(Some(List(1)), Some(2))) // 2