Reputation: 61
I'm trying to understand the use of the 'implicitly' operation:
class foo {
def x : Int = 12
}
object foo {
implicit def intToFoo(x : Int ) : foo = new foo
}
object bar {
implicitly[foo](1).x
}
The above compiles. But I can't work out why the 'implicitly' operation is required. It seems to me that because the 'destination companion object' includes the implicit conversion, it should automatically happen.
Upvotes: 0
Views: 157
Reputation: 44967
It's not really required here. You could also get away with
object bar {
import foo._
1.x
}
because importing foo._
would also bring the implicit conversion into the implicit scope, so that 1
would be wrapped into a foo
automatically, thereby "acquiring" the method x
.
The usage of implicitly
in your code is rather uncommon, because it is essentially just a replacement for a type ascription:
object bar {
(1: foo).x
}
The type ascription works too, because now you again mention that you expect 1
to be of type foo
, and therefore the compiler will search in the implicit scope of foo
for a suitable implicit conversion.
Even if you don't import any implicit conversions, your code does not rely on implicitly
in any significant way. The same would happen with any other function that requires a foo
:
import scala.language.implicitConversions
class foo {
def x : Int = 12
}
object foo {
implicit def intToFoo(x : Int ) : foo = new foo
}
def randomOtherFunction(a: foo, i: Int): foo = a
object bar {
randomOtherFunction(1, 42).x
}
Here, just as in your example with implicitly
, the function randomOtherFunction
requires a foo
as first argument. Since Int
is not a foo
, the compiler goes on to search for an applicable implicit conversion from Int
to foo
. One of the places where it looks is the companion object of foo
. Therefore, the compiler inserts intToFoo
:
randomOtherFunction(intToFoo(1), 42).x
and everything compiles.
Upvotes: 1
Reputation: 51271
One of the more common uses for implicitly
(that I've seen) is when employing context bound type parameters.
Recall that a type parameter can be restricted to a type class by requiring "evidence":
def f[N](n :N)(implicit ev :Numeric[N]) :Boolean = ???
This can be made cleaner by using context bound syntax.
def f[N :Numeric](n: N) :Boolean = ???
But now we don't have ev
, the implicit parameter, to work with. This isn't always a problem. Often you need the type restriction but you don't need the evidence parameter. But if you do need the parameter then one way around the problem is to pull it in via implicitly
.
def f[N :Numeric](n: N) :Boolean = {
val nn = implicitly[Numeric[N]].negate(n)
. . .
}
This adds a bit of overhead since the implicit is resolved twice, once as the (now hidden) implicit parameter and once again via implicitly
, but if you want the clean context bound syntax, and only need the implicit parameter once or twice, it is sometimes seen as a worthwhile trade off.
Upvotes: 2