The reason why you see
Error: "Type mismatch. Required: String, found: T"
is that by default Scala 2 defines the following implicit method in Predef
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
(this would not compile in Scala 3). If you wanted to write single non-generic calculator class that would work for all integral types (whatever that means for you - Scala doesn't have inheritance hierarchy that would restrict types to just Int, Byte, Long). You would use your own Integer typeclass
class Calculator {
def add[A: Integer](lhs: A, rhs: A): A = implicitly[Integer[A]].plus(lhs, rhs)
}
with a following definition
trait Integer[A] {
def plus(lhs: A, rhs: A): A
}
object Integer {
implicit val Int: Integer[Int] = new Integer[Int] {
override def plus(lhs: Int, rhs: Int): Int = lhs + rhs
}
// note explicit toByte cast -> byte addition can overflow!
// (thus result type is Int and you might want to stick
// to the original type in your use case)
implicit val Byte: Integer[Byte] = new Integer[Byte] {
override def plus(lhs: Byte, rhs: Byte): Byte = (lhs + rhs).toByte
}
}
To make it look a bit nicer, you might leverage extension methods.
class Calculator {
import Integer._
def add[A: Integer](lhs: A, rhs: A): A = lhs + rhs
}
object Integer {
implicit class IntegerOps[A: Integer](lhs: A) {
def +(rhs: A): A = implicitly[Integer[A]].plus(lhs, rhs)
}
// the rest of the object stays the same
}
and with Scala 3, you can write it even more concisely
class Calculator {
def add[A: Integer](lhs: A, rhs: A): A = lhs + rhs
}
trait Integer[A]:
extension(a: A) def +(b: A): A
given Integer[Int] with
extension(a: Int) def +(b: Int): Int = a + b
given Integer[Byte] with
extension(a: Byte) def +(b: Byte): Byte = (a + b).toByte
Edit: per @Tim suggestion you can use Integral type class that is predefined. I will still leave my answer as a reference in case you wanted to implement similar typeclass by yourself.