Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to paramaterize Int as Ordered in scala

Tags:

scala

I have a class with a parameterized type that I want to do comparison operators on. I assmue I need to use the Ordered trait to achieve this but the compiler doesn't like my use of it. So say I have the following class:

class Test[T <: Ordered[T]] {

  def someOp(t: T) if(t < 3) ...
  ...
}

However if I try to use this class as follows:

val test = new Test[Int]()

the compiler complains as follows:

type arguments [Test[Int]] do not conform to class Test's type parameter bounds [T <: Ordered[T]]

Can someone explain to me what I am doing wrong here?

like image 276
user79074 Avatar asked Oct 15 '13 15:10

user79074


2 Answers

This occurs because Int is not a subclass of Ordered[Int] (see here why).

However, there is an implicit coercion from Int to RichInt which is a subclass of Ordered[Int], but it isn't triggered for lower bounds. Use <% (view bounds) instead which will consider implicit coercions:

class Test[T <% Ordered[T]]
like image 112
thSoft Avatar answered Oct 31 '22 18:10

thSoft


You can use the Ordering[T] type class as an implicit parameter. If you wanted to write a generic max function, it would look like this:

def max[T](a:T, b:T)(implicit ordering:Ordering[T]) = { 
  if(ordering.gt(a,b)) a else b 
}

For the primitive data types like Int, Float, ..., there is an implicit Ordering[T] in scope so that you can use this as you would expect.

max(1,2) // gives 2

For all types that implement Ordered[T], there is also an implicit to provide an Ordering[T].

There are also various methods in scope that combine orderings. For example if you have a N-tuple where each element has an Ordering[T], there automatically exists an Ordering for the tuple type.

max((1,2), (3,4)) // gives (3,4) because 3 is larger than 1

But if you are not satisfied with any of the implicitly provided orderings you can just write your own and pass it explicitly or even get it in scope as an implicit val. Like this:

val negativeIntOrdering = new Ordering[Int] { 
  def compare(a:Int,b:Int) = b - a 
}

max(1,2)(negativeIntOrdering) // gives 1

So the typeclass based approach is much more flexible than the inheritance based approach. This is why math libraries like spire use it extensively.

One thing that is not as nice about the code above is that you have to use the lt method instead of an operator. But there is a solution for that as well. Ordering has an implicit method called mkOrderingOps that provides operators for T. You just need to get it in scope by importing ordering._, like this:

def max[T](a:T, b:T)(implicit ordering:Ordering[T]) = { 
  import ordering._; 
  if(a>b) a else b 
}
like image 14
Rüdiger Klaehn Avatar answered Oct 31 '22 17:10

Rüdiger Klaehn