The code snippet below is taken from this ScalaZ tutorial.
I cannot figure out how the implicit resolution rules are applied when evaluating 10.truthy
at the bottom of the code example.
Things that - I think - I do understand are the following:
1) The implicit value intCanTruthy
is an instance of an anonymous subclass of CanTruthy[A]
which defines the truthys
method for Int
-s according to :
scala> implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys({
case 0 => false
case _ => true
})
intCanTruthy: CanTruthy[Int] = CanTruthy$$anon$1@71780051
2) The toCanIsTruthyOps
implicit conversion method is in scope when evaluating 10.truthy
, so the compiler will try to use this implicit conversion method when it sees that Int
does not have a truthy
method. So the compiler will try to look for some implicit conversion method which converts 10
into an object that does have a truthy
method and therefor it will try toCanIsTruthyOps
to this conversion that.
3) I suspect that the implicit value intCanTruthy
somehow might be used when the compiler tries the toCanIsTruthyOps
implicit conversion on 10
.
But this is where I really get lost. I just don't see how the implicit resolution process proceeds after this. What happens next ? How and Why ?
In other words, I don't know what is the implicit resolution sequence that allows the compiler to find the implementation of the truthy
method when evaluating 10.truthy
.
Questions:
How will 10
be converted to some object which does have the correct truthy
method ?
What will that object be ?
Where will that object come from?
Could someone please explain, in detail, how the implicit resolution takes place when evaluating 10.truthy
?
How does the self-type { self => ...
in CanTruthy
play a role in the implicit resolution process ?
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait CanTruthy[A] { self =>
/** @return true, if `a` is truthy. */
def truthys(a: A): Boolean
}
object CanTruthy {
def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev
def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] {
def truthys(a: A): Boolean = f(a)
}
}
trait CanTruthyOps[A] {
def self: A
implicit def F: CanTruthy[A]
final def truthy: Boolean = F.truthys(self)
}
object ToCanIsTruthyOps {
implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =
new CanTruthyOps[A] {
def self = v
implicit def F: CanTruthy[A] = ev
}
}
// Exiting paste mode, now interpreting.
defined trait CanTruthy
defined module CanTruthy
defined trait CanTruthyOps
defined module ToCanIsTruthyOps
Trying out the type class on 10
:
scala> import ToCanIsTruthyOps._
import ToCanIsTruthyOps._
scala> implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys({
case 0 => false
case _ => true
})
intCanTruthy: CanTruthy[Int] = CanTruthy$$anon$1@71780051
scala> 10.truthy
res6: Boolean = true
First of all, thanks for pasting a fully self-contained example.
How will
10
be converted to some object which does have the correcttruthy
method ?
When calling a method on a value whose type A
does not supply that method, an implicit conversion must kick in, i.e. there must be a method or function in scope with signature A => B
with B
having the method in question (truthy
). In the case of a conversion method, it may ask for additional implicit parameters which would be looked up accordingly.
The conversion method is toCanIsTruthyOps
, made available by importing the contents of ToCanIsTruthyOps
. The type B
in the aforementioned sentence then is CanTruthyOps
, and the conversion method is toCanIsTruthyOps
. It may be invoked by the compiler as long as the implicit type-class evidence parameter CanTruthy
is found. So since type A = Int
, the compiler has to find an implicit value of type CanTruthy[Int]
if the call 10.truthy
is going to be successful.
It looks for such a value in several places. It could be in the companion object of Int
(that doesn't exist) or the companion object of CanTruthy
, or it was explicitly imported into the current scope. Here, the last case is used. You explicitly created the implicit value intCanTruthy
, and that value is now found. And that's it.
What will that object be ?
There will be a temporary instance of CanTruthyOps
whose mere purpose is to call truthys
on the evidence type class value (your intCanTruthy
here).
Where will that object come from?
It is found in the look-up for implicit conversions Int => CanTruthyOps[Int]
. The conversion performs the instantiation of that object.
Could someone please explain, in detail, how the implicit resolution takes place when evaluating
10.truthy
?
See answer to first question above. Or as explicit code:
type A = Int
val v: A = 10
val ev: CanTruthy[A] = intCanTruthy
val ops: CanTruthyOps[A] = ToCanIsTruthyOps.toCanIsTruthyOps(v)(ev)
ops.truthy
How does the self-type
{ self => ...
inCanTruthy
play a role in the implicit resolution process ?
It has nothing to do with the implicit resolution. Indeed, in your example of the trait CanTruthy
, the self
acts as an alias for this
and isn't even used, so you could just remove it.
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