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

# What's the reason behind this kind of erasure?

3 replies
ojow
Joined: 2009-10-21,

Why class Bad has double definition and class Ok doesn't?
What's the logic behind this kind of erasure?

trait A
trait B
trait C
class Ok {
def m(v: B with A) = 1;
def m(v: C with A) = 1;
}
class Bad {
def m(v: A with B) = 1;
def m(v: A with C) = 1;
}

David Hall 4
Joined: 2009-08-21,
Re: What's the reason behind this kind of erasure?

On Fri, Dec 18, 2009 at 10:44 AM, Oleg G wrote:
> Why class Bad has double definition and class Ok doesn't?
> What's the logic behind this kind of erasure?
>
> trait A
> trait B
> trait C
> class Ok {
>  def m(v: B with A) = 1;
>  def m(v: C with A) = 1;
> }
> class Bad {
>  def m(v: A with B) = 1;
>  def m(v: A with C) = 1;
> }
>

The JVM only allows 1 type for each method argument, so Scala has to
make a choice about what type to use. So it chooses the first type.
That is, "X with Y" has to be erased to X. You've already identified
the work around.

ichoran
Joined: 2009-08-14,
Re: What's the reason behind this kind of erasure?
I can see a proximal logic to this, but not any deep logic.

(Sorry for the nonstandard notation--I don't know if there is an operator or function defined for linearization of class hierarchy.)

The proximal logic is that if you have a linearization ||_|| of a mixin type
||B with A|| -> { B , A , AnyRef , Any }
then you can pass any class C such that
x in {B,A,AnyRef,Any} implies x in ||C||
In particular A with B is perfectly okay too, because
||A with B|| -> { A , B , AnyRef , Any }
and each of those things is in ||B with A||.

On the other hand, the linearization of the type hierarchy is a quite a mess to use for every operation, so overloading apparently only looks at the first type as a simplification.

This leads to other weirdnesses also:

trait A { def me = "A" }
trait B extends A { override def me = "B" }
trait C extends A { override def me = "C" }
object Unusable {
def f(x: B with C) = x.me.toUpperCase
def f(x: C with B) = x.me.toLowerCase
}

scala> Unusable.f( new B with C {} )
<console>:12: error: ambiguous reference to overloaded definition,
both method f in object Unusable of type (x: C with B)java.lang.String
and  method f in object Unusable of type (x: B with C)java.lang.String
match argument types (java.lang.Object with B with C)
Unusable.f( new B with C {} )
^

scala> Unusable.f( new C with B {} )
<console>:12: error: ambiguous reference to overloaded definition,
both method f in object Unusable of type (x: C with B)java.lang.String
and  method f in object Unusable of type (x: B with C)java.lang.String
match argument types (java.lang.Object with C with B)
Unusable.f( new C with B {} )
^

scala> Unusable.f( (new C with B {}):(C with B) )
<console>:12: error: ambiguous reference to overloaded definition,
both method f in object Unusable of type (x: C with B)java.lang.String
and  method f in object Unusable of type (x: B with C)java.lang.String
match argument types (C with B)
Unusable.f( (new C with B {}):(C with B) )
^

So I guess the question is why the overloading resolver allows definitions that end in ambiguity with the type checker.

--Rex

On Fri, Dec 18, 2009 at 1:44 PM, Oleg G <ojowoo [at] gmail [dot] com> wrote:
Why class Bad has double definition and class Ok doesn't?
What's the logic behind this kind of erasure?

trait A
trait B
trait C
class Ok {
def m(v: B with A) = 1;
def m(v: C with A) = 1;
}
class Bad {
def m(v: A with B) = 1;
def m(v: A with C) = 1;
}

ojow
Joined: 2009-10-21,
Re: What's the reason behind this kind of erasure?

Thanks. I was suspecting something of that kind. Just wanted to be
sure that the 'workaround' isn't wrong.

2009/12/19 David Hall :
> On Fri, Dec 18, 2009 at 10:44 AM, Oleg G wrote:
>> Why class Bad has double definition and class Ok doesn't?
>> What's the logic behind this kind of erasure?
>>
>> trait A
>> trait B
>> trait C
>> class Ok {
>>  def m(v: B with A) = 1;
>>  def m(v: C with A) = 1;
>> }
>> class Bad {
>>  def m(v: A with B) = 1;
>>  def m(v: A with C) = 1;
>> }
>>
>
> The JVM only allows 1 type for each method argument, so Scala has to
> make a choice about what type to use. So it chooses the first type.
> That is, "X with Y" has to be erased to X. You've already identified
> the work around.
>

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