Operators on Parameterized Type in Scala
For a higher order function that takes a function of type (A, A) => Boolean as a parameter:
def isSorted[A](as: Array[A], ordered : (A,A) => Boolean) : Boolean =
{
def doSorted(i: Int) : Boolean =
if (i >= as.length-1) true
else if (ordered(as(i), as(i+1))) false
else doSorted(i+1)
doSorted(0)
}
val lst1 = Array(1,2,3,4,5,6)
I can declare a function with known parameter types and pass it to:
def gtr(a : Int, b : Int) : Boolean = {
a > b
}
isSorted(lst1,gtr) /* works :-) */
I would like to do one of the following. None of these seem to work for me:
-
Use a function with parameterized types:
def gtr[T](a : T, b : T) : Boolean = { a > b /* gives value > is not a member of type parameter T */ }
Is this possible in Scala. Should I tell the compiler that T is inherited from an object that has a p> operator
-
Use an anonymous function:
isSorted(lst1, ((x,y) => x > y)) /* gives missing parameter type */
-
Use Scala underscore mask to pass operator>
isSorted(lst1, _>_) /* gives missing parameter type */
None of these three options work for me and I am struggling to figure out why. Can anyone tell me which of the above approaches are valid in Scala and what I am doing wrong.
source to share
The problem is that your type parameter A
has no restrictions, which means you can pass any type. But this cannot be, because not every type has a method >
, as you say. There is also no generic type that other types inherit from >
. The only option is class classes for which we have an Ordering class . You will need to require to Ordering[A]
exist (implicitly) in your method signature.
I'll do this for a method greater
with one caveat. We will need to curry the parameters isSorted
.
def isSorted[A](as: Array[A])(ordered: (A, A) => Boolean): Boolean = {
def doSorted(i: Int) : Boolean =
if (i >= as.length - 1) true
else if (ordered(as(i), as(i + 1))) false
else doSorted(i + 1)
doSorted(0)
}
We now define greater
to implicitly find Ordering[A]
. Once we get one, we can use its method gt
for the actual comparison.
def greater[A: Ordering](a: A, b: A): Boolean = implicitly[Ordering[A]].gt(a, b)
This is the same as:
def greater[A](a: A, b: A)(implicit ord: Ordering[A]): Boolean = ord.gt(a, b)
Using:
scala> isSorted(Array(1, 2, 3, 4))(greater _)
res88: Boolean = true
scala> isSorted(Array(1, 4, 3, 4))(greater _)
res89: Boolean = false
source to share
You're right. You need to indicate that T can be compared. One approach is to use view boundaries:
def gtr[T <% Ordered[T]](a : T, b : T) : Boolean = {
a > b
}
[T <% Ordered[T]]
can be read as " can be seen as " (extended or implicitly converted). This approach is now deprecated https://issues.scala-lang.org/browse/SI-7629 ), so you can use implicit proofs to convert to T
Ordered[T]
T
Ordered[T]
def gtr[T](a : T, b : T)(implicit toOrdered: T => Ordered[T]) : Boolean
A very nice post that explains the scope of the overview in more detail: What are the scope and scope of Scala?
Regarding your 2nd and 3rd questions: in the second example, you need to infer the types for x and y in order to compile the code. 3rd is just syntactic sugar for 2nd. Both of them will compile.
isSorted(Array(1,2,3,4,5,6), ((x : Int,y: Int) => x > y))
isSorted(Array(1,2,3,4,5,6), ((_: Int) > (_: Int)))
source to share