0

I started studying recently Scala. And I don't understand why this code is not working. Can anybody help me?

import scala.collection.mutable
import scala.collection.mutable.Map

class Bijection[T] (val v: Set[T]) {
  private var addition = 0
  private var reversed = false

  def add(i: Int)(implicit ev: T <:< Int) = {
    addition += i
  }

  def reverse(implicit ev: T <:< String) = {
    reversed = !reversed
  }
}

object Bijection {
  def apply(s: Set[String]): Bijection[String] = {
    println("Hello string")
    Bijection(s)
  }

  def apply[T](s: Set[T]): Bijection[T] = {
    println("Hello T")
    Bijection(s)
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    var x = Bijection[String](Set[String]("fd", "fasf"))
  }
}

This code should print "Hello" But I get now next trace.

Error:(23, 7) double definition:
def apply(s: Set[String]): Bijection[String] at line 18 and
def apply[T](s: Set[T]): Bijection[T] at line 23
have same type after erasure: (s: scala.collection.immutable.Set)Bijection
  def apply[T](s: Set[T]): Bijection[T] = {
archi1998
  • 13
  • 4
  • 3
    Using new will not call the apply method in main. As the above comment mention you will need to remove the new keyward to call Bijection.apply. (you don't need the [String] type parameter) Finally you will also need to explicitly or implictly provide the expected parameter ignore – Terry Dactyl May 23 '20 at 15:06
  • @user you are genius! I am very grateful to you for this question and previous. – archi1998 May 23 '20 at 15:15
  • I update question @user can you see that? – archi1998 May 23 '20 at 15:24
  • 1
    You can't use just `Bijection(s)` in your apply method because that will call the apply method *again*, causing recursion. Instead, call the actual constructor there, and make your primary constructor private. – user May 23 '20 at 15:26
  • 1
    Yes but error is not in recursion now. – archi1998 May 23 '20 at 15:37

1 Answers1

0

The JVM doesn't have reified generics, instead, it erases type parameters at runtime, even though they're stored in bytecode. The only reason it has type parameters are to check things at compile time. Because of this, your 2 apply methods will both have the signature.

def apply(s: Set): Bijection

at runtime.

Because of this, there's no way for the right apply method to be chosen.

Furthermore, you have to call the constructor inside the apply method. If you don't use new, then you'll just call the same apply method again, going into infinite recursion.

Instead, you can do something like this. If it's not a string, the implicit parameter will remain null, and you will know that it is not a String but some other T.

object Bijection {
  private def applyString(s: Set[String]): Bijection[String] = {
    println("Hello String")
    new Bijection(s)
  }
  
  private def applyT[T](s: Set[T]): Bijection[T] = {
    println("Hello T")
    new Bijection(s)
  }
  
  def apply[T](s: Set[T])(implicit ev: Set[T] =:= Set[String] = null): Bijection[T] =
    if (ev == null) applyT(s) else applyString(s).asInstanceOf[Bijection[T]]
}

You don't have to change anything else.

Scastie

user
  • 7,435
  • 3
  • 14
  • 44