2

Consider the follwing generic function:

def compare[I:Ordering,T:Ordering](i:I,t:T):Int

It should compare a value of type I with a value of type T with both of them assumed to have Ordering defined. The comparison should work if there is either a way to implicitly convert I to T, or T to I. Obviously, if one uses types I and T that do not have any of the two conversions, the compiler should complain.

I am tempted to write something like this:

def compare[I:Ordering,T:Ordering](i:I,t:T)(implicit c1:I=>T, c2:T=>I):Int

But this actually asks for both conversions to exist, not at least one.

Any ideas?

EDIT: Given the comments I want to make the question complete. If both implicit conversions exist, I would like to assume a priority among the types. Then use the higher priority implicit conversion for the comparison.

Krle
  • 70
  • 7

1 Answers1

0

Wrong Answer which I wrote initially: Of course it will ask because you are trying to compare two different ordering. T:Ordering means that there should be an Ordering[T] available in the scope. Ordering[T] is different from Ordering[I]. It is like comparing numbers and strings where both can be ordered differently but ordering together does not makes sense.

PS: Both numbers and strings can be ordered together but that means numbers & strings will represent the same datatype here and there will be only one instance of Ordering for that data type.

Better answer: Use a wrapper class to define the converters

object Main extends App {
  def compare[I: Ordering, T: Ordering](i: I, t: T)(implicit wrapper:   Wrapper[I, T]): Int = {
    val converter: Either[(I) => T, (T) => I] = wrapper.getConverterBasedOnPriority
    val convertedValue = if(converter.isLeft){
      converter.left.map(c => c(i))
    } else{
      converter.right.map(c => c(t))
    }
    // do what ever you want
    1
  }

  val iToT: (Int => String) = i => i.toString
  val tToI: (String => Int) = s => s.toInt

//  implicit def iToTWrapper = new Wrapper[Int , String ](iToT, null)
  implicit def tToIWrapper = new Wrapper[Int , String ](null, tToI)

  compare(1, "a")
}

class Wrapper[I, T](iToT: I => T, tToI : T => I) {
  def getConverterBasedOnPriority:Either[I => T, T => I]   = {
    // return ordering based on priority check.
    // returning iToT for example sake. Do the priority check and return accordingly
    Left(iToT)
  }
}

If you uncomment both implicits, it will throw and error. If you comment both implicits, it will throw and error.

Aravindh S
  • 1,185
  • 11
  • 19
  • You haven't answered my question and I think I was pretty clear. W – Krle Oct 20 '16 at 10:06
  • One thing you are right is that have not considered if both implicit conversions actually exist, but in that case there should be a defined priority among the types. I'll edit the question – Krle Oct 20 '16 at 10:08
  • My Bad. I didnt understand properly. Modifying the type signature to `(implicit c1: I => T = null, c2: T => I = null)` with default values will solve your problem. But with the above signature, the code will compile even if there are no implicit conversions in scope. Also you need to do null check before using the implicits. I am not exactly sure on how to make this throw error when no implicit exists. – Aravindh S Oct 20 '16 at 10:14
  • Yes, you are right. I just tried it and it works, but the runtime checks are a no-no for me. :) – Krle Oct 20 '16 at 10:23
  • As I think about it, it's like a generalization of the fact that you can do both `3l>2` and `3>2l`, but without overloading `>` in every type for every other type as a parameter. – Krle Oct 20 '16 at 10:29
  • One more idea I can think of is to write a wrapper class. I am editing my old answer – Aravindh S Oct 20 '16 at 10:47
  • Thanks! For the record, I found this answer to be pretty nice: http://stackoverflow.com/questions/15735352/compare-two-numbers – Krle Oct 21 '16 at 13:55