Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the type of "Banana with Cream" the same as "Banana" after erasure ? How can it be fixed?

Tags:

scala

I want different method binding (at compile-time !) depending on a marker trait (like Cream). How can this be achieved ?

My solution below does not compile.

How can it be fixed ?

class Apple
class Banana
trait Cream

object HasOverloadedMethods
{
  def method(p:Apple)=println("Apple")
  def method(p:Banana)=println("Banana")
  def method(p:Banana with Cream)=println("Banana with Cream")
}

object Question extends App{
  HasOverloadedMethods.method(new Apple())
  HasOverloadedMethods.method(new Banana())
  HasOverloadedMethods.method(new Banana() with Cream)
}

error:

double definition:
method method:(p: Banana with Cream)Unit and
method method:(p: Banana)Unit at line 9
have same type after erasure: (p: Banana)Unit
  def method(p:Banana with Cream)=println("Banana with Cream")
      ^
like image 689
jhegedus Avatar asked Nov 20 '25 01:11

jhegedus


1 Answers

Unfortunately you can't fix it like that, since the JVM doesn't know mixin types (and the bytecode signature of method(x: Banana with Cream) is therefore only method(x: Banana)).

You have a couple of options, which all have their drawbacks.

  1. Only take a Cream. This makes you loose the Banana
  2. Create a BananaWithCream trait. This clutters your hierarchy.
  3. Use type-classes:

    def method[T : MethImpl](x: T) = implicitly[MethImpl[T]].impl(x)
    
    trait MethImpl[T] {
      def impl(x: T): Unit
    }
    
    trait LowPrioMethImpl {
      implicit object BananaImpl extends MethImpl[Banana] {
        def impl(x: Banana) = println("Banana")
      }
    }
    
    object MethImpl extends LowPrioMethImpl {
      implicit object AppleImpl extends MethImpl[Apple] {
        def impl(x: Apple) = println("Apple")
      }
    
      implicit object BananaWithCreamImpl extends MethImpl[Banana with Cream] {
        def impl(x: Banana with Cream) = println("Banana with Cream")
      }
    }
    

    Now you can:

    method(new Banana) // > Banana
    method(new Banana with Cream) // > Banana with Cream
    method(new Apple) // > Apple
    
    method("adsf") // error: Could not find implicit value ...
    

    The con is obviously the clutter this solution introduces.

like image 52
gzm0 Avatar answered Nov 21 '25 16:11

gzm0