7

I need to get a generic check method that can be used as follows:

check[Int](10, 1, 5) and check[Double](10.0, 1.0, 5.0).

I tried this code:

trait RangeChecker {
  def check[T <: AnyVal](value:T, mini:T, maxi:T) : Boolean = {
    (value >= mini && value <= maxi)
  }
}

However, I get Cannot resolve symbol errors for >=, && and <=. What might be wrong?

enter image description here

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
prosseek
  • 182,215
  • 215
  • 566
  • 871

3 Answers3

11

When you write

def check[T <: AnyVal] ...

you're defining the method for all the subtypes of AnyVal. However, your implementation uses two methods (<= and >=) which are available only for a subset of types, namely the ones that support ordering.

So you have to specify that the method applies to all types for which an ordering exists, or in other words

def check[T](value: T, min: T, max: T)(implicit ev: T => Ordered[T]): Boolean =
  value >= min && value <= max

This syntax is equivalent to a view bound (<%)

def check[T <% Ordered[T]](value: T, min: T, max: T): Boolean = ...

but, since view bounds are deprecated, you should avoid it.


Another option is to use Ordering in this fashion

def check[T](value: T, mini: T, maxi: T)(implicit ord: Ordering[T]): Boolean = {
  import ord.mkOrderingOps
  value >= mini && value <= maxi
}

where importing ord.mkOrderingOps gives you the ability of using the regular >= and <= methods.

Another equivalent alternative using a context bound directly:

def check[T: Ordering](value: T, mini: T, maxi: T): Boolean = {
  val ord = implicitly[Ordering[T]]
  import ord.mkOrderingOps
  value >= mini && value <= maxi
}
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • @samthebest, thanks for noting `mkOrderingOps`, which is nicer than importing the whole thing. – Gabriele Petronella Aug 12 '14 at 12:57
  • Why rollback my edit on the question though - your solution is very neat and general, and so I'm hoping to make it easier for people googling to find your answer. Your answer is the nicest explanation of Context Bounds and View Bounds on SO - it deserves SEO. – samthebest Aug 12 '14 at 12:58
  • That edit of yours totally changed the sense of the question. The OP didn't even know about context bounds and view bounds. There's plenty of guides and questions about using traits and context bounds for achieving type classes in scala, so it's not so critical that this pops up. If you feel like, you can always ask a general question on SO and even give a generic answer yourself. – Gabriele Petronella Aug 12 '14 at 13:01
  • Fair point. I only just found this: http://stackoverflow.com/questions/4465948/what-are-scala-context-and-view-bounds – samthebest Aug 12 '14 at 13:12
  • http://stackoverflow.com/questions/4373070/how-do-i-get-an-instance-of-the-type-class-associated-with-a-context-bound – Gabriele Petronella Aug 12 '14 at 13:16
3

Try the following:

def check[T <% Ordered[T]](value: T, mini: T, maxi: T): Boolean = {
    (value >= mini && value <= maxi)
  }

The <= and >= operators are defined as part of the Ordered trait, not AnyVal.

EDIT: see the scala API docs here and here

EDIT2: Replaced <: with <%

ffxtian
  • 559
  • 2
  • 5
  • Could you include an example of calling this method? I'm getting "type mismatch" errors when I try it with `Int`. – Dan Getz Aug 11 '14 at 17:39
  • Oh yikes... I'm getting the same thing. I forgot to try an implementation test before I posted (I assumed it would work, since it compiled *facepalm*). IMO, your answer's a bit more elegant, in any case. – ffxtian Aug 11 '14 at 17:42
  • 2
    Looks like it works with a view bound (`<%` instead of `<:`). – Dan Getz Aug 11 '14 at 17:48
  • I just finished checking that and was about to comment -- I'll edit the post. – ffxtian Aug 11 '14 at 17:50
  • 1
    View bounds are deprecated, see [my answer](http://stackoverflow.com/a/25250693/846273) – Gabriele Petronella Aug 11 '14 at 19:18
3

AnyVals are not required to support operations like >=. That's why it won't work like you tried. Unfortunately, there is no superclass of all the orderable AnyVals.

You should use Ordering instead:

def check[T](value: T, low: T, high: T)(implicit ord: Ordering[T]): Boolean = {
  ord.gteq(value, low) && ord.lteq(value, high)
}

You often won't need to define the type T explicitly when you call it, either:

scala> check(1, 2, 3)
res0: Boolean = false
scala> check(3, 2, 3.5)
res1: Boolean = true
Dan Getz
  • 8,774
  • 6
  • 30
  • 64