This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Why is this use of implicits not ambiguous?

17 replies
Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.

Please have a look at the following code. I can't figure out why the
calls aren't ambiguous. Why is it better to convert an int to a
Fraction than an int and Fraction to a double? And why is it better
the other way around in the second case?

Thanks,

Cay

object Fraction {
def apply(n: Int, d: Int) = new Fraction(n, d)
implicit def int2Fraction(n: Int) = Fraction(n, 1)
implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
}

class Fraction(val num: Int, val den: Int) {
def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
override def toString = num + "/" + den
}

object Main extends App {
val f = Fraction(3, 4)
println(f * 5) // 15/4
println(5 * f) // 3.75

def mul(a: Double, b: Double) = a * b
def mul(a: Fraction, b: Fraction) = a * b

println(mul(f, 5)) // 15/4
println(mul(5, f)) // 15/4
}

Lex
Joined: 2010-02-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

It seems the compiler assumes that Integers have a method *(Double)
defined, so it uses the implicit conversion from Faction to Double. It
looks like a bug, since it is inconsistent with any other function
calls that involve Doubles.

I don't know if this can be changed, since there may be code that
depends on this quirky behavior.

On Fri, Jun 24, 2011 at 2:29 AM, Cay Horstmann wrote:
> Please have a look at the following code. I can't figure out why the
> calls aren't ambiguous. Why is it better to convert an int to a
> Fraction than an int and Fraction to a double? And why is it better
> the other way around in the second case?
>
> Thanks,
>
> Cay
>
> object Fraction {
>  def apply(n: Int, d: Int) = new Fraction(n, d)
>  implicit def int2Fraction(n: Int) = Fraction(n, 1)
>  implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
> }
>
> class Fraction(val num: Int, val den: Int) {
>  def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
>  override def toString = num + "/" + den
> }
>
> object Main extends App {
>  val f = Fraction(3, 4)
>  println(f * 5) // 15/4
>  println(5 * f) // 3.75
>
>  def mul(a: Double, b: Double) = a * b
>  def mul(a: Fraction, b: Fraction) = a * b
>
>  println(mul(f, 5)) // 15/4
>  println(mul(5, f)) // 15/4
> }
>

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Why is this use of implicits not ambiguous?

On Fri, Jun 24, 2011 at 04:29, Cay Horstmann wrote:
> Please have a look at the following code. I can't figure out why the
> calls aren't ambiguous. Why is it better to convert an int to a

I can't figure out why do you think they are, so let me explain what I
think is happening, and you can explain what you think should happen.

> Fraction than an int and Fraction to a double? And why is it better
> the other way around in the second case?
>
> Thanks,
>
> Cay
>
> object Fraction {
>  def apply(n: Int, d: Int) = new Fraction(n, d)
>  implicit def int2Fraction(n: Int) = Fraction(n, 1)
>  implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
> }
>
> class Fraction(val num: Int, val den: Int) {
>  def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
>  override def toString = num + "/" + den
> }
>
> object Main extends App {
>  val f = Fraction(3, 4)
>  println(f * 5) // 15/4

f is a Fraction which implements the method "*", so the only implicit
that comes into play is one that turn the parameter to method * into a
valid parameter: Int => Fraction

>  println(5 * f) // 3.75

5 is an Int which implements the method "*", so the only implicit that
comes into play is one that turn the parameter to method * into a
valid parameter: Fraction => Double. Mind you, * on Int is overloaded
to accept an AnyVal number types
(http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/...).

>  def mul(a: Double, b: Double) = a * b
>  def mul(a: Fraction, b: Fraction) = a * b
>
>  println(mul(f, 5)) // 15/4

Here with have one parameter which is Fraction and one which is Int.
There's an implicit which makes this parameter list valid, which is
Int => Fraction. If you passed two Int instead, then *after* the
implicit conversions failed to yield a valid parameter list, *then*
type widening would take place.

>  println(mul(5, f)) // 15/4

Same as above.

> }
>

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

2011/6/24 Daniel Sobral :
> On Fri, Jun 24, 2011 at 04:29, Cay Horstmann wrote:
>> Please have a look at the following code. I can't figure out why the
>> calls aren't ambiguous. Why is it better to convert an int to a
>
> I can't figure out why do you think they are, so let me explain what I
> think is happening, and you can explain what you think should happen.

Thanks very much! I will gladly take you up on that. Maybe you can
point me to the parts of the language spec that I should consult.

>> object Fraction {
>>  def apply(n: Int, d: Int) = new Fraction(n, d)
>>  implicit def int2Fraction(n: Int) = Fraction(n, 1)
>>  implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
>> }
>>
>> class Fraction(val num: Int, val den: Int) {
>>  def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
>>  override def toString = num + "/" + den
>> }
>>
>> object Main extends App {
>>  val f = Fraction(3, 4)
>>  println(f * 5) // 15/4
>
> f is a Fraction which implements the method "*", so the only implicit
> that comes into play is one that turn the parameter to method * into a
> valid parameter: Int => Fraction

Indeed, when considering f.*(5). If that were not to work out (e.g.
when removing int2Fraction), the compiler is willing to consider
fractionToDouble(f).*(5).

What I am looking for is a statement in the spec that says to first
try f.*(5), and stop when that succeeds.

>
>>  def mul(a: Double, b: Double) = a * b
>>  def mul(a: Fraction, b: Fraction) = a * b
>>
>>  println(mul(f, 5)) // 15/4
>
> Here with have one parameter which is Fraction and one which is Int.
> There's an implicit which makes this parameter list valid, which is
> Int => Fraction. If you passed two Int instead, then *after* the
> implicit conversions failed to yield a valid parameter list, *then*
> type widening would take place.

Alternatively, of course, one can convert f to a Double and 5 to a
Double. I agree that this looks worse than just converting 5 to a
Fraction, but what are the rules? Is there some kind of scoring
system? The only scoring system I could find in the spec is in 6.26.3,
and it did not seem to apply here (but maybe I don't understand it
right--it's pretty dense).

Thanks,

Cay

Lex
Joined: 2010-02-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

> Indeed, when considering f.*(5). If that were not to work out (e.g.
> when removing int2Fraction), the compiler is willing to consider
> fractionToDouble(f).*(5).
>
> What I am looking for is a statement in the spec that says to first
> try f.*(5), and stop when that succeeds.

I have no idea where it is hidden in the spec, but if you stick with
scala long enough, you will find out that the rules are as follows.

The steps compiler takes to resolve a method call on an object:
1. Check if there is a method that can be called without conversions.
2. If no such method exists, check if there are any conversions that
can be applied to the ARGUMENT that will allow to call the method.
3. If no such conversions exists, check if the object on which the
method is called can be converted.

When considering 5.*(Fraction), the compiler stop at step 2, because
if you look at the spec for Int you will find that method *(Double) is
defined.
http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/...

> Alternatively, of course, one can convert f to a Double and 5 to a
> Double. I agree that this looks worse than just converting 5 to a
> Fraction, but what are the rules? Is there some kind of scoring
> system? The only scoring system I could find in the spec is in 6.26.3,
> and it did not seem to apply here (but maybe I don't understand it
> right--it's pretty dense).

5 is never converted to a Double, there lies the rub.

On Fri, Jun 24, 2011 at 8:00 PM, Cay Horstmann wrote:
> 2011/6/24 Daniel Sobral :
>> On Fri, Jun 24, 2011 at 04:29, Cay Horstmann wrote:
>>> Please have a look at the following code. I can't figure out why the
>>> calls aren't ambiguous. Why is it better to convert an int to a
>>
>> I can't figure out why do you think they are, so let me explain what I
>> think is happening, and you can explain what you think should happen.
>
> Thanks very much! I will gladly take you up on that. Maybe you can
> point me to the parts of the language spec that I should consult.
>
>>> object Fraction {
>>>  def apply(n: Int, d: Int) = new Fraction(n, d)
>>>  implicit def int2Fraction(n: Int) = Fraction(n, 1)
>>>  implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
>>> }
>>>
>>> class Fraction(val num: Int, val den: Int) {
>>>  def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
>>>  override def toString = num + "/" + den
>>> }
>>>
>>> object Main extends App {
>>>  val f = Fraction(3, 4)
>>>  println(f * 5) // 15/4
>>
>> f is a Fraction which implements the method "*", so the only implicit
>> that comes into play is one that turn the parameter to method * into a
>> valid parameter: Int => Fraction
>
> Indeed, when considering f.*(5). If that were not to work out (e.g.
> when removing int2Fraction), the compiler is willing to consider
> fractionToDouble(f).*(5).
>
> What I am looking for is a statement in the spec that says to first
> try f.*(5), and stop when that succeeds.
>
>>
>>>  def mul(a: Double, b: Double) = a * b
>>>  def mul(a: Fraction, b: Fraction) = a * b
>>>
>>>  println(mul(f, 5)) // 15/4
>>
>> Here with have one parameter which is Fraction and one which is Int.
>> There's an implicit which makes this parameter list valid, which is
>> Int => Fraction. If you passed two Int instead, then *after* the
>> implicit conversions failed to yield a valid parameter list, *then*
>> type widening would take place.
>
> Alternatively, of course, one can convert f to a Double and 5 to a
> Double. I agree that this looks worse than just converting 5 to a
> Fraction, but what are the rules? Is there some kind of scoring
> system? The only scoring system I could find in the spec is in 6.26.3,
> and it did not seem to apply here (but maybe I don't understand it
> right--it's pretty dense).
>
> Thanks,
>
> Cay
>

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

> I have no idea where it is hidden in the spec, but if you stick with
> scala long enough, you will find out that the rules are as follows.
>
> The steps compiler takes to resolve a method call on an object:
> 1. Check if there is a method that can be called without conversions.
> 2. If no such method exists, check if there are any conversions that
> can be applied to the ARGUMENT that will allow to call the method.
> 3. If no such conversions exists, check if the object on which the
> method is called can be converted.

Indeed--that's exactly what is happening. So, I overload resolution
never enters the picture because there is just one * for each of the
two cases.

>>> def mul(a: Double, b: Double) = a * b
>>> def mul(a: Fraction, b: Fraction) = a * b
>>> val f = Fraction(3, 4)
>>> println(mul(f, 5))

>> Alternatively, of course, one can convert f to a Double and 5 to a
>> Double. I agree that this looks worse than just converting 5 to a
>> Fraction, but what are the rules? Is there some kind of scoring
>> system? The only scoring system I could find in the spec is in 6.26.3,
>> and it did not seem to apply here (but maybe I don't understand it
>> right--it's pretty dense).
>
> 5 is never converted to a Double, there lies the rub.

Really? Comment out the int2Fraction implicit, and you get

scala.this.Predef.println(Main.this.mul(Fraction.fraction2Double(Main.this.f),
5.0))

in the -Xprint:typer output.

That's why this situation troubles me more. In this case, overload
resolution must enter the picture. There are two functions mul. Both
of them are applicable. Both are defined in the same class, so none is
prioritized one the other. On what basis is one chosen over the other?

To make matters more confusing, consider this minor change (again,
with both implicits)

def mul(a: Double, b: Float) = a * b // was b: Double before
def mul(a: Fraction, b: Fraction) = a * b
val f = new Fraction(3, 4)
println(mul(f, 5))

Now it's ambiguous! It's willing to consider converting 5 into 5.0F
but not 5.0? I don't get it.

Thanks,

Cay

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Why is this use of implicits not ambiguous?

On Fri, Jun 24, 2011 at 22:00, Cay Horstmann wrote:
> 2011/6/24 Daniel Sobral :
>> On Fri, Jun 24, 2011 at 04:29, Cay Horstmann wrote:
>>> Please have a look at the following code. I can't figure out why the
>>> calls aren't ambiguous. Why is it better to convert an int to a
>>
>> I can't figure out why do you think they are, so let me explain what I
>> think is happening, and you can explain what you think should happen.
>
> Thanks very much! I will gladly take you up on that. Maybe you can
> point me to the parts of the language spec that I should consult.
>
>>> object Main extends App {
>>>  val f = Fraction(3, 4)
>>>  println(f * 5) // 15/4
>>
>> f is a Fraction which implements the method "*", so the only implicit
>> that comes into play is one that turn the parameter to method * into a
>> valid parameter: Int => Fraction
>
> Indeed, when considering f.*(5). If that were not to work out (e.g.
> when removing int2Fraction), the compiler is willing to consider
> fractionToDouble(f).*(5).

So, all the confusion seems to be coming from this point. Actually, if
f.*(5) doesn't work out, then it tries fractionToDouble(f).*(5 widened
to Double). 5 is an Int, not a Double. It can only be used as a Double
through type widening. Likewise, a Double is not an Int.

All of this is under section 6.26, and, more precisely, section 6.26.1
Value Conversions. The implicit resolution is part of Overloading
Resolution, it seems to me (it's the only place where "compatible" is
used, that being the definition on 6.26 that mentions implicit views).
Converting from Int to Double is part of Type Widening, the next step.

I'll admit I don't see anything that says the conversions have
precedence over each other. If you eliminate the need for type
widening, though (ie, make everything Double or everything Int),
you'll see the ambiguity errors arising. I presume. :-)

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

> So, all the confusion seems to be coming from this point. Actually, if
> f.*(5) doesn't work out, then it tries fractionToDouble(f).*(5 widened
> to Double). 5 is an Int, not a Double. It can only be used as a Double
> through type widening. Likewise, a Double is not an Int.
>
> All of this is under section 6.26, and, more precisely, section 6.26.1
> Value Conversions. The implicit resolution is part of Overloading
> Resolution, it seems to me (it's the only place where "compatible" is
> used, that being the definition on 6.26 that mentions implicit views).
> Converting from Int to Double is part of Type Widening, the next step.
>
> I'll admit I don't see anything that says the conversions have
> precedence over each other.  If you eliminate the need for type
> widening, though (ie, make everything Double or everything Int),
> you'll see the ambiguity errors arising. I presume. :-)

Ok, let's look at this in the context of my example

def mul(a: Double, b: Double) = a * b
def mul(a: Fraction, b: Fraction) = a * b
val f = new Fraction(3, 4)
val result = mul(f, 5)) // What is mul here?

6.26.1 starts out with overload resolution. Let's do it.

The set A consists of the two mul functions.

Now we compute the shapes. Both have shape (Nothing, Nothing).

I suppose they are both "applicable", so the set B still has both functions.

I don't understand the next step. What does it mean to "type each argument
with an undefined expected type"? Anyway, whatever it may mean, it
can't tell Double
and Fraction apart, so I assume that the set C is the same as the set B.

Now we have four conditions for specifying that two methods are "as
specific as" another.
These are also quite unclear, but since in the end the compiler gives preference
to one of the mul methods, I don't think they matter.

Next is the "relative weight" computation. It talks about where the
methods are defined.
They are both defined in the same class, so that yields no preference.

That gets us to "It is an error if there is no alternative in C which
is more specific than all other
alternatives in C ." So, my case must be an error, and it's a bug in
the compiler that it picks one
over another.

But that can't be right. At no point did I ever get to look at the
types that were involved.

Clearly, I don't understand this section at all. I can't even see how
Example 6.26.1 works.
In which step is f(b, b) matched against the first definition of f?

Compare all this against JLS 15.12.2.5. That describes a "more
specific" relationship that
involves the parameter types in somewhat mind-numbing detail, as one
would expect.

I'd be very grateful if someone can tell me where I am mis-reading the spec.

Thanks,

Cay

Lex
Joined: 2010-02-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

> That gets us to "It is an error if there is no alternative in C which
> is more specific than all other
> alternatives in C ." So, my case must be an error, and it's a bug in
> the compiler that it picks one
> over another.

The method mul(Fraction, Fraction) is more specific when invoking
mul(f, 5) because it requires only one implicit conversion, from 5 to
Fraction. The other method mul(Double, Double) would require two
implicit conversions, from Fraction to Double and from 5 to Double.
So the reason it's not ambiguous is because 5 is an Integer and
requires an implicit conversion to a Double. Moreover, if you had an
implicit conversion from Double to Fraction, then the compiler would
give you an error, just like you expect. Try adding this implicit to
your code: implicit def double2Fraction(n: Double) = Fraction(1, 1)

> I'd be very grateful if someone can tell me where I am mis-reading the spec.

Most of your troubles are related to misunderstanding primitive type
definitions and conversions, not from applying the spec. Try rewriting
these examples with your own types that have a limited set of methods
and conversions, maybe then everything will become clear.

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

> The method mul(Fraction, Fraction) is more specific when invoking
> mul(f, 5) because it requires only one implicit conversion, from 5 to
> Fraction. The other method mul(Double, Double) would require two
> implicit conversions, from Fraction to Double and from 5 to Double.
> So the reason it's not ambiguous is because 5 is an Integer and
> requires an implicit conversion to a Double.

Your explanation does not explain why the following IS ambiguous.

Cheers,

Cay

object Fraction {
def apply(n: Int, d: Int) = new Fraction(n, d)
implicit def int2Fraction(n: Int) = Fraction(n, 1)
implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
}

class Fraction(n: Int, d: Int) {
val num: Int = if (d == 0) 1 else n / gcd(n, d);
val den: Int = if (d == 0) 0 else d / gcd(n, d);
def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
override def toString = num + "/" + den
private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}

object Main extends App {
def mul(a: Double, b: Float) = a * b
def mul(a: Fraction, b: Fraction) = a * b

val f = new Fraction(3, 4)
println(mul(f, 5))
}

Florian Hars 3
Joined: 2011-05-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

I am beginning to see that there is indeed something surprising going on.
I think it is pretty obvious that

println(mul(f,5))

should be ambiguous if the only definitions of mul in scope are

def mul(a: Double, b: Int) = a * b
def mul(a: Fraction, b: Fraction) = a * b

because both are consistent with the expected return type Any and both
have one argument that matches exactly and one that requires an implicit
conversion. If we change the first definition to

def mul(a: Double, b: Long) = a * b

the second argument 5 does no longer match exactly, but it is still weakly
conformant and the compiler still considers the call ambiguous. So weak
conformance does not seem to count as an implicit conversion. The same
thing happens if we go one step further anlong the weak conformance
relation and change the first mul to

def mul(a: Double, b: Float) = a * b

The second argument still matches via numeric wideing and the first
argument still requires an implicit conversion for this mul to match and
the function call is still ambiguous. Only if we take yet another step
to the top of the weak conformance hierarchy and say

def mul(a: Double, b: Double) = a * b

the compiler does something different, even though the second argument
should still match via numeric widening and the first still requires an
implicit, so nothing relevant seems to have changed.

Seen from that point, the behaviour *is* surprising.

I think it is also surprising that a method that requires an implicit on
its return type is as specific as one that does not:

| both method mul in object Main of type (a: Fraction, b: Fraction)Fraction
| and method mul in object Main of type (a: Double, b: Int)Double
| match argument types (Fraction,Int) and expected result type Double
| println(mul(f, 5) : Double)
| ^

- Florian.

Jan van der Vorst
Joined: 2011-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

Surely this proofs it, what else can it be but a bug?
I would suggest to file a bug report.

regards,
Jan van der Vorst

On Jun 26, 8:56 am, Cay Horstmann wrote:
> > The method mul(Fraction, Fraction) is more specific when invoking
> > mul(f, 5) because it requires only one implicit conversion, from 5 to
> > Fraction. The other method mul(Double, Double) would require two
> > implicit conversions, from Fraction to Double and from 5 to Double.
> > So the reason it's not ambiguous is because 5 is an Integer and
> > requires an implicit conversion to a Double.
>
> Your explanation does not explain why the following IS ambiguous.
>
> Cheers,
>
> Cay
>
> object Fraction {
>   def apply(n: Int, d: Int) = new Fraction(n, d)
>   implicit def int2Fraction(n: Int) = Fraction(n, 1)
>   implicit def fraction2Double(f: Fraction) = f.num * 1.0 / f.den
>
> }
>
> class Fraction(n: Int, d: Int) {
>   val num: Int = if (d == 0) 1 else n / gcd(n, d);
>   val den: Int = if (d == 0) 0 else d / gcd(n, d);
>   def *(other: Fraction) = new Fraction(num * other.num, den * other.den)
>   override def toString = num + "/" + den
>   private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
>
> }
>
> object Main extends App {
>   def mul(a: Double, b: Float) = a * b
>   def mul(a: Fraction, b: Fraction) = a * b
>
>   val f = new Fraction(3, 4)
>   println(mul(f, 5))
>
> }

Jan van der Vorst
Joined: 2011-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

Surely this proves it, what else can it be but a bug?
I would suggest to file a bug report.

regards,
Jan van der Vorst

On Jun 26, 2:38 pm, Florian Hars wrote:
> I am beginning to see that there is indeed something surprising going on.
> I think it is pretty obvious that
>
> println(mul(f,5))
>
> should be ambiguous if the only definitions of mul in scope are
>
>   def mul(a: Double, b: Int) = a * b
>   def mul(a: Fraction, b: Fraction) = a * b
>
> because both are consistent with the expected return type Any and both
> have one argument that matches exactly and one that requires an implicit
> conversion. If we change the first definition to
>
>   def mul(a: Double, b: Long) = a * b
>
> the second argument 5 does no longer match exactly, but it is still weakly
> conformant and the compiler still considers the call ambiguous. So weak
> conformance does not seem to count as an implicit conversion. The same
> thing happens if we go one step further anlong the weak conformance
> relation and change the first mul to
>
>   def mul(a: Double, b: Float) = a * b
>
> The second argument still matches via numeric wideing and the first
> argument still requires an implicit conversion for this mul to match and
> the function call is  still ambiguous. Only if we take yet another step
> to the top of the weak conformance hierarchy and say
>
>   def mul(a: Double, b: Double) = a * b
>
> the compiler does something different, even though the second argument
> should still match via numeric widening and the first still requires an
> implicit, so nothing relevant seems to have changed.
>
> Seen from that point, the behaviour *is* surprising.
>
> I think it is also surprising that a method that requires an implicit on
> its return type is as specific as one that does not:
>
> | both method mul in object Main of type (a: Fraction, b: Fraction)Fraction
> | and  method mul in object Main of type (a: Double, b: Int)Double
> | match argument types (Fraction,Int) and expected result type Double
> |   println(mul(f, 5) : Double)
> |           ^
>
> - Florian.
>
> --
> #!/bin/sh -
> set - `type -p $0` 'tr [a-m][n-z]RUXJAKBOZ [n-z][a-m]EH$W/@OBM' fu XUBZRA.fvt\
> angher echo;while [ "$5" != "" ];do shift;done;$4 "gbhpu $3;fraqznvy sKunef.q\
> r<$3&&frq -a -rc "`$4 "$0"|$1`">$3;rpub 'Jr ner Svt bs Obet.'"|$1|`$4 $2|$1`

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Why is this use of implicits not ambiguous?

I'm sorry, I was completely wrong in my explanation. The error with
"Fraction" actually makes it clear for me.

On Sat, Jun 25, 2011 at 21:48, Cay Horstmann wrote:
>

Non-ambiguous:

>  def mul(a: Double, b: Double) = a * b
>  def mul(a: Fraction, b: Fraction) = a * b
>  val f = new Fraction(3, 4)
>  val result = mul(f, 5)) // What is mul here?

Ambiguous:

> def mul(a: Double, b: Float) = a * b
> def mul(a: Fraction, b: Fraction) = a * b
> val f = new Fraction(3, 4)
> println(mul(f, 5))

The explanation is, indeed, in 6.23.3, but it is not related to type
widening. It took me a while to figure it out, and I was finally clued
in by some implicit searches I saw with -Ytyper-debug that I did not
understand.

The problem is specificity. Specifically, is one of the mul methods
more specific than the other? There's a single paragraph that explains
the behavior here:

A parameterized method m of type (p1 : T1, . . . , pn : Tn)U is as
specific as some
other member m' of type S if m' is applicable to arguments (p1, . . . , pn ) of
types T1, . . . , Tn.

So, say mul(Double, Double)Double is m and mul(Fraction,
Faction)Fraction is m'. For m to be as specific as m', we have to know
if m' is applicable to (Double, Double). Since Double does not conform
to Fraction, then m is not as specific as m'.

For m' to be as specific as m, we check if m is applicable to
(Fraction, Fraction). Now, Fraction _does_ conform to Double, since
there's an implicit view from Fraction to Double.

Next, we see if one of them is _more specific_ than the other. It
involves computing a simple value from 0 to 2 for each alternative:

The relative weight of an alternative A over an alternative B is a
number from 0 to 2,
defined as the sum of
• 1 if A is as specific as B, 0 otherwise, and
• 1 if A is defined in a class or object which is derived from the
class or object
defining B, 0 otherwise.

So, m' has 1, and m has 0, meaning m' (mul(Fraction,
Fraction)Fraction) is more specific than m (mul(Double,
Double)Double).

What happens when you introduce mul(Double, Float) is that Double does
not conform to Float, so neither method is more specific than the
other, resulting in an ambiguity.

Cay Horstmann
Joined: 2009-09-04,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

2011/6/27 Daniel Sobral :
> What happens when you introduce mul(Double, Float) is that Double does
> not conform to Float, so neither method is more specific than the
> other, resulting in an ambiguity.

You mean that Fraction doesn't conform to Float, right?

That was an excellent explanation, and I finally understood the
meanings of of "as specific as" and "parameterized method" in the
spec.

Thanks,

Cay

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Why is this use of implicits not ambiguous?

On Sun, Jun 26, 2011 at 21:21, Cay Horstmann wrote:
> 2011/6/27 Daniel Sobral :
>> What happens when you introduce mul(Double, Float) is that Double does
>> not conform to Float, so neither method is more specific than the
>> other, resulting in an ambiguity.
>
> You mean that Fraction doesn't conform to Float, right?

Err, yes, right. Damn... I kept writing "Fraction" instead of "Float",
and having to go back and fix it everything, only to write "Double"
instead of "Fraction"! :-)

> That was an excellent explanation, and I finally understood the
> meanings of of "as specific as" and "parameterized method" in the
> spec.

Florian Hars 3
Joined: 2011-05-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Why is this use of implicits not ambiguous?

On Sun, Jun 26, 2011 at 05:52:23AM -0700, Jan van der Vorst wrote:
> Surely this proves it, what else can it be but a bug?
> I would suggest to file a bug report.

Except that the reasoning I showed is not the reasoning the compiler
employs (as Daniel has shown elsewhere in this thread), so there is no
bug, just a behaviour that can be surprising depending on your mental
model of what the compiler should do (which often is not what the
compiler actually does around the edges of the type system, since it
has to care about such annoying nitty gritty stuff as consistency
and correctness).

- Florian

Jan van der Vorst
Joined: 2011-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is this use of implicits not ambiguous?

An excellent lesson indeed.

Adding yet another implicit Double2Fraction results again in
ambiguity. This would be explained by Daniels explanation too since
neither of the two mult methods would in that case be more specific
than the other.

thanks Daniel,

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland