Type parameter to ensure relational operators

Viewed 68

I have been trying to determine how one might write a type parameter that restricts a function to types that support relational operators?

For example:

def biggerOf[A <: ???](a: A, b: A): A = { if (a > b) a else b }

Where ??? is my dilemma. Advanced type parameter bits are new to me, so asking for a little help. Thought AnyVal might be a winner but for Unit type (and Boolean which won't break, but won't work either). Thanks for any ideas.

3 Answers

You want to bring the Ordering typeclass into play.

import scala.math.Ordering.Implicits.infixOrderingOps

def biggerOf[A:Ordering](a: A, b: A): A = { if (a > b) a else b }

A:Ordering restricts A to types in the Ordering typeclass and infixOrderingOps enables the convenience operators (methods) such as <, >=, etc.

You can use the ordering typeclass.

def biggerOf[A : Ordering](a: A, b: A): A = {
  import Ordering.Implicits._
  if (a > b) a else b
}

As Luis and jwvh has suggested, you can use the Ordering typeclass but I'd like to introduce to you the typeclass pattern and using typeclasses in restricting the usage of a function in a more depth.

A typeclass allows one to be able to be flexible in its generic types whilst restricting it enough that those that do not have an instance of the typeclass may not be able to use your function.

Take this as an example, assuming that the Numerical datatypes in Scala have no operators on them, we can generalize the idea of operating on them by introducing the Num typeclass and then by specifying their behavior on the operation because these Numerical datatypes have different ways of handling algebraic operations. (e.g an Integral vs Real number)

trait Num[A] {

  def add(l: A, r: A): A
  def sub(l: A, r: A): A
  def mul(l: A, r: A): A
  def div(l: A, r: A): A

}

object NumTest {
  def addThenMultiply[A](l: A, r: A)(implicit ev: Num[A]): A =
    ev.mul(ev.add(l, r), r)

  //then we create Num instances for numerical types

  implicit val intNum = new Num[Int] { /* implementation*/ }
  implicit val floatNum = new Num[Float] { /* implementation */ }
}

Now in this case, the function addThenMultiply will only work for Int and Float datatypes since they're the only ones that have a Num instance but you can add instances for other custom datatypes also depending on your need.

Related