0

I know this is the umpteenth implicits / companion object question. However, I did not find this case anywhere yet.

In "Tryout", why does A need no import for the implicit class, while B does need it? Thankx.

class LongConsumer {
  def consume(l: Long) = Unit
}

case class X(x: Long)

object X extends (Long => X) {
  implicit class ConsumeX(val c: LongConsumer) extends AnyVal {
    def consume(x: X) = c consume x.x
  }
  implicit class EatX(val c: LongConsumer) extends AnyVal {
    def eat(x: X) = c consume x.x
  }
}

object Tryout {
  // A: does not need import to compile - why?
  new LongConsumer().consume(X(10L))

  // B: needs import to compile - why?
  import X.EatX
  new LongConsumer().eat(X(10L))
}
Georg
  • 764
  • 6
  • 22

1 Answers1

1

There are only three cases for views.

http://www.scala-lang.org/files/archive/spec/2.11/07-implicit-parameters-and-views.html#views

Your case A is #1, where X is not a Long, as expected by consume. (Edit: Sorry, I'm watching Hulu. It's #3, where consume doesn't apply. The conversion is from LongConsumer to ConsumeX. For that case, I wouldn't have expected the implicit scope of X to be in play.)

But notice that the implicit scope is the scope for X => Long.

The scope of Function[X, Long] includes the scope of both type params, and the scope of X includes the companion X. (The previous section, 7.2, lists what's in the implicit scope.) The intuition is that you have one type in hand but need the other; either type can provide the conversion.

In your case B, it's case #2, and the scope of LongConsumer doesn't supply a conversion.

I asked on the ML for clarification. -Ytyper-debug says it's the second try.

|    |    second try: (l: Long)Unit.type and eatery.X.type
|    |    |-- eatery.X.type EXPRmode (silent: value <local Tryout> in Tryout)
|    |    |    |-- X.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Tryout> in Tryout)
|    |    |    |    \-> (x: Long)eatery.X
|    |    |    \-> eatery.X
|    |    |-- eatery.this.X.ConsumeX BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Tryout> in Tryout) implicits disabled
|    |    |    \-> eatery.X.ConsumeX.type <and> (c: eatery.LongConsumer)eatery.X.ConsumeX
|    |    |-- (c: eatery.LongConsumer)eatery.X.ConsumeX EXPRmode-POLYmode-QUALmode (site: value <local Tryout> in Tryout)
|    |    |    \-> eatery.X.ConsumeX
|    |    |-- eatery.this.X.ConsumeX(new LongConsumer()).consume BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Tryout> in Tryout)
|    |    |    \-> (x: eatery.X)Unit.type (with underlying type (x: eatery.X)Unit.type)
|    |    \-> Unit.type
|    \-> [object Tryout] eatery.Tryout.type

Results are in:

https://issues.scala-lang.org/browse/SI-5089

@retronym says: The broader question of why the implicit scope includes companions of the argument types of the applications is known as the "1 + BigInteger(1) problem"

Kind of obvious when you put it that way.

Also see enter link description here under "Implicit scope of an argument's type."

Community
  • 1
  • 1
som-snytt
  • 38,672
  • 2
  • 41
  • 120
  • My guess is the spec lies for case #3. The implicit scope isn't only `T` (here `LongConsumer`), but `T` and the types of the missing parameters (so also including `X` here) – wingedsubmariner Jul 15 '14 at 14:19
  • As I understand it now, is case A: the LongConsumer object has a consume method, however, the argument type does not fit. Therefore, the scopes of BOTH LongConsumer AND argument are searched. In case B however, the LongConsumer has not eat method, therefore it has no reason to search the argument scope for a conversion. – Georg Jul 15 '14 at 15:00
  • Updated with the relevant issue. The intuition for case 3 is the same as for case 1. – som-snytt Jul 15 '14 at 16:13