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))
}
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."