Michael Wrighton
Michael Wrighton

Reputation: 61

why and when is 'implicitly' required

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

Answers (2)

Andrey Tyukin
Andrey Tyukin

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

jwvh
jwvh

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

Related Questions