0

I was trying to create a Generic Calculator in Scala which can work on all Integer types. When I am trying the following code it gives me an error

Error: "Type mismatch. Required: String, found: T"

class Calculator[T <: Integral[T]] {
  def add[T](a: T, b: T): T = a + a
}

Can someone please explain to me how come it was expecting String type?

1
  • 2
    What are all these "Integer types" ? Commented Jan 16, 2022 at 8:25

2 Answers 2

3

To answer the specific question, the type of a + b is String because Scala has a default + that works on any object by converting both arguments to a String and concatenating them. But what this is saying is that there is no other method + on a that can be used.

The broader problem is that Numeric is not the base class of all numeric types, but rather a typeclass that defines numeric behaviour on a type T. So the class defintion should look like this:

class Calculator[T : Integral] {

This tells the compiler that you can't create an instance of Calculator[T] unless there is an implicit instance of Integral[T] visible to the compiler at the time.

So there is an instance of Integral[T] that has methods that operate on T to do the basic numeric operations (plus, times, negate etc.). The next question is, how do I get hold of this instance of Integral[T]? The answer to that is implicitly, which will return the typeclass value for a type as long as it is available (otherwise it is a compile time error).

So the expression implicitly[Integral[T]] will return an instance of Integral[T] for whatever type was used to create the Calculator. And that instance knows how to plus values of type T. Once you know this, you can write your add function:

class Calculator[T : Integral] {
  def add(a: T, b: T): T = implicitly[Integral[T]].plus(a, b)
}

[Also note that there is no type parameter on add because the type parameter is on the class, so it is not def add[T](...) but just def add(...)]

Sign up to request clarification or add additional context in comments.

Comments

1

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.

2 Comments

Why define your own typeclass when Integral already exists?
good point @Tim :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.