I have read the answer to my question about scala.math.Integral but I do not understand what happens when Integral[T]
is passed as an implicit parameter. (I think I understand the implicit parameters concept in general).
Let's consider this function
import scala.math._
def foo[T](t: T)(implicit integral: Integral[T]) { println(integral) }
Now I call foo
in REPL:
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@581ea2
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@17fe89
How does the integral
argument become scala.math.Numeric$IntIsIntegral
and scala.math.Numeric$LongIsIntegral
?
In simpler terms, if no value or parameter is passed to a method or function, then the compiler will look for implicit value and pass it further as the parameter. For example, changing an integer variable to a string variable can be done by a Scala compiler rather than calling it explicitly.
Unless the call site explicitly provides arguments for those parameters, Scala will look for implicitly available given (or implicit in Scala 2) values of the correct type. If it can find appropriate values, it automatically passes them.
What Are Implicit Parameters? Implicit parameters are similar to regular method parameters, except they could be passed to a method silently without going through the regular parameters list. A method can define a list of implicit parameters, that is placed after the list of regular parameters.
The short answer is that Scala finds IntIsIntegral
and LongIsIntegral
inside the object Numeric
, which is the companion object of the class Numeric
, which is a super class of Integral
.
Read on for the long answer.
Implicits in Scala refers to either a value that can be passed "automatically", so to speak, or a conversion from one type to another that is made automatically.
Speaking very briefly about the latter type, if one calls a method m
on an object o
of a class C
, and that class does not support method m
, then Scala will look for an implicit conversion from C
to something that does support m
. A simple example would be the method map
on String
:
"abc".map(_.toInt)
String
does not support the method map
, but StringOps
does, and there's an implicit conversion from String
to StringOps
available (see implicit def augmentString
on Predef
).
The other kind of implicit is the implicit parameter. These are passed to method calls like any other parameter, but the compiler tries to fill them in automatically. If it can't, it will complain. One can pass these parameters explicitly, which is how one uses breakOut
, for example (see question about breakOut
, on a day you are feeling up for a challenge).
In this case, one has to declare the need for an implicit, such as the foo
method declaration:
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
There's one situation where an implicit is both an implicit conversion and an implicit parameter. For example:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
The method getIndex
can receive any object, as long as there is an implicit conversion available from its class to Seq[T]
. Because of that, I can pass a String
to getIndex
, and it will work.
Behind the scenes, the compile changes seq.IndexOf(value)
to conv(seq).indexOf(value)
.
This is so useful that there is a syntactic sugar to write them. Using this syntactic sugar, getIndex
can be defined like this:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
This syntactic sugar is described as a view bound, akin to an upper bound (CC <: Seq[Int]
) or a lower bound (T >: Null
).
Please be aware that view bounds are deprecated from 2.11, you should avoid them.
Another common pattern in implicit parameters is the type class pattern. This pattern enables the provision of common interfaces to classes which did not declare them. It can both serve as a bridge pattern -- gaining separation of concerns -- and as an adapter pattern.
The Integral
class you mentioned is a classic example of type class pattern. Another example on Scala's standard library is Ordering
. There's a library that makes heavy use of this pattern, called Scalaz.
This is an example of its use:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
There is also a syntactic sugar for it, called a context bound, which is made less useful by the need to refer to the implicit. A straight conversion of that method looks like this:
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Context bounds are more useful when you just need to pass them to other methods that use them. For example, the method sorted
on Seq
needs an implicit Ordering
. To create a method reverseSort
, one could write:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.reverse.sorted
Because Ordering[T]
was implicitly passed to reverseSort
, it can then pass it implicitly to sorted
.
When the compiler sees the need for an implicit, either because you are calling a method which does not exist on the object's class, or because you are calling a method that requires an implicit parameter, it will search for an implicit that will fit the need.
This search obey certain rules that define which implicits are visible and which are not. The following table showing where the compiler will search for implicits was taken from an excellent presentation about implicits by Josh Suereth, which I heartily recommend to anyone wanting to improve their Scala knowledge.
Let's give examples for them.
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
This is like the first example, but assuming the implicit definition is in a different file than its usage. See also how package objects might be used in to bring in implicits.
There are two object companions of note here. First, the object companion of the "source" type is looked into. For instance, inside the object Option
there is an implicit conversion to Iterable
, so one can call Iterable
methods on Option
, or pass Option
to something expecting an Iterable
. For example:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield, (x, y)
That expression is translated by the compile into
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
However, List.flatMap
expects a TraversableOnce
, which Option
is not. The compiler then looks inside Option
's object companion and finds the conversion to Iterable
, which is a TraversableOnce
, making this expression correct.
Second, the companion object of the expected type:
List(1, 2, 3).sorted
The method sorted
takes an implicit Ordering
. In this case, it looks inside the object Ordering
, companion to the class Ordering
, and finds an implicit Ordering[Int]
there.
Note that companion objects of super classes are also looked into. For example:
class A(val n: Int)
object A {
implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b // s == "A: 2"
This is how Scala found the implicit Numeric[Int]
and Numeric[Long]
in your question, by the way, as they are found inside Numeric
, not Integral
.
This is required to make the type class pattern really work. Consider Ordering
, for instance... it comes with some implicits in its companion object, but you can't add stuff to it. So how can you make an Ordering
for your own class that is automatically found?
Let's start with the implementation:
class A(val n: Int)
object A {
implicit val ord = new Ordering[A] {
def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
}
}
So, consider what happens when you call
List(new A(5), new A(2)).sorted
As we saw, the method sorted
expects an Ordering[A]
(actually, it expects an Ordering[B]
, where B >: A
). There isn't any such thing inside Ordering
, and there is no "source" type on which to look. Obviously, it is finding it inside A
, which is a type parameter of Ordering
.
This is also how various collection methods expecting CanBuildFrom
work: the implicits are found inside companion objects to the type parameters of CanBuildFrom
.
I haven't actually seen examples of this. I'd be grateful if someone could share one. The principle is simple:
class A(val n: Int) {
class B(val m: Int) { require(m < n) }
}
object A {
implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b // s == "B: 3"
I'm pretty sure this was a joke. I hope. :-)
EDIT
Related questions of interest:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With