Greg
Greg

Reputation: 11542

How can I mathematically compare two unknown types in Scala?

I'm writing a little script evaluator function. It takes two arguments and a comparison operator like this:

    def compare[T,U](a:T, op:String, b:U): Boolean = {
      op match {
        case "==" => a == b
        case "<"  => a < b
        // and so on with other comparators...
      }
    }

This code won't compile. The '<' operator doesn't work on the generic types. I couldn't find a parent class for numeric types with the '<' operator, so I can't even do something like: def compare[T<:Numeric,U<:Numeric](...)

Is there a way to do this (or a library)? Right now I can only test equal/not-equal.

Upvotes: 5

Views: 396

Answers (2)

Jeremy Owens
Jeremy Owens

Reputation: 159

edit: Luis' answer involving using a single parameter type and relying on type inference is likely the best overall solution. If you are set on using two separate types, here are some options:

Since you are attempting to compare two separate classes, you cannot directly inherit a trait member implementation that will fit both. However, if you can provide function literals that will convert a class to a Double, you can use the following code.

def compare[T,U](a: T, op:String, b: U, tToDouble: T => Double, uToDouble: U => Double): Boolean = {
      op match {
        case "==" => tToDouble(a) == uToDouble(b)
        case "<"  => tToDouble(a) < uToDouble(b)
        // and so on with other comparators...
      }
    }
//example using Int
println(compare(1, "<", 2, (x:Int) => x.toDouble, (y:Int) => y.toDouble)) //true

Unfortunately, we cannot use Ordering or Numeric, because those are parameterized traits and the expectation is for comparison against themselves. Another method is just to accept a comparator function that accepts objects of each type whenever you wish to compare two types of objects.

def compare[T,U](a: T, op:String, b: U, comparator: (T,U) => Int): Boolean = {
      op match {
        case "==" => comparator(a,b) == 0
        case "<"  => comparator(a,b) < 0
        // and so on with other comparators...
      }
    }

println(compare[Int, Int](1, "<", 2, (a,b) => a-b)) //true

Upvotes: 2

In general, every time you want some kind of interface for common operations on multiple types, the answer is a Typeclass.
In this particular case, you can use scala.math.Ordering.

import scala.math.Ordering
import Ordering.Implicits._

def compare[T: Ordering](a: T, op: String, b: T): Boolean = op match {
  case "==" => a == b
  case "<"  => a < b
  // and so on with other comparators...
}

And now you can use it like this.

compare(10, "==", 15) //false
compare(10, "==", 10) // true
compare(10, "<", 10) // false
compare(10, "<", 11) // true

Upvotes: 6

Related Questions