In Programming in Scala (5th ed), page 462, there is the following code:
def isort[T](xs: List[T])(using ord: Ord[T]): List[T] =
if xs.isEmpty then Nil
else insert(xs.head, isort(xs.tail))
def insert[T](x: T, xs: List[T])(using ord: Ord[T]): List[T] =
if xs.isEmpty || ord.lteq(x, xs.head) then x :: xs
else xs.head :: insert(x, xs.tail)
where Ord[T]
is defined as:
trait Ord[T]:
def compare(x: T, y: T): Int
def lteq(x: T, y: T): Boolean = compare(x, y) < 1
I was trying to understand the need for the using parameter list in the isort
function. I worked it out with chatgpt (reasonably well). It seems to make sense in the case where these are defined in one file and are called by a function in another file. In that case, the Ord[T]
isn't defined in the compilation unit so it can't be provided to insert
unless it first is provided to isort
.
However, I then asked if the using param list is still required if the given
(along with the calling function - like main) was also defined (or imported) in the same file. In that case it would be available to the insert
function in the same way it is available to a calling function.
So, I tried the following in a single-file application:
trait Ord[T]:
def compare(x: T, y: T): Int
def lteq(x: T, y: T): Boolean = compare(x, y) < 1
given intOrd: Ord[Int] with
def compare(x: Int, y: Int): Int = x - y
def isort[T](xs: List[T]): List[T] =
if xs.isEmpty then Nil
else insert(xs.head, isort(xs.tail))
def insert[T](x: T, xs: List[T])(using ord: Ord[T]): List[T] =
if xs.isEmpty || ord.lteq(x, xs.head) then x :: xs
else xs.head :: insert(x, xs.tail)
object Given {
def main(args: Array[String]): Unit = {
println(isort(List(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5)))
}
}
Here, I removed the (using ord: Ord[T])
from the isort
function definition. And, like chatgpt seemed to indicate, I get the error:
No given instance of type Ord[T] was found for parameter ord of method insert
else insert(xs.head, isort(xs.tail))
If I add the using
parameter list back into the isort
function definition then it works as expected.
I don't understand why main
can find the proper given
but isort
cannot. The definition is in scope for all three functions: isort
, insert
, and main
. Why is it not visible to isort
and insert
?
I don't understand why
main
can find the propergiven
butisort
cannot.
Because in main
you need Ord[Int]
and it's defined as intOrd
. But insert[T]
and therefore isort[T]
needs Ord[T]
(for abstract T
aka arbitrary T
) and such instance isn't defined (*). So you need to assume (with (using ord: Ord[T])
) that it will be present upon a call (when T
is inferred to be Int
).
(*) You can check that if you add
given tOrd[T]: Ord[T] with
def compare(x: T, y: T): Int = ???
then (using ord: Ord[T])
wouldn't be necessary but such instance (the same for arbitrary T
) doesn't make much sense.
BTW, if you put instances of a type class to its companion object
trait Ord[T]:
...
object Ord:
given intOrd ...
...
then such instances can be found in other compilation units without import.