5

Can someone clarify why the following code causes a MatchError? What does a MatchError mean in this case?

class A {
  def g = A.f
}

object A {
  val f = "Object A"
}


class B extends A {
  override val A.f = "Object B"
}

val b = new B
b.g

Given this doesn't work, is there a way to override a companion object val or def similar to this?

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
deepkimo
  • 3,187
  • 3
  • 23
  • 21

2 Answers2

3

First, to why you're seeing a MatchError. A value on an object (A.f) is considered a stable identifier (as Scala reference says, "Stable members are [...] members introduced by object definitions or by value definitions of non-volatile types").

Here's what the typer output looks like:

object A extends scala.AnyRef {
    ...
    private[this] val f: String = "Object A";
    <stable> <accessor> def f: String = A.this.f
}

When used in an assignment, the compiler "desugars" assignment of this stable identifier (it being stable is a necessary condition) into pattern matching:

<synthetic> private[this] val x$1: Unit = ("Object B": String("Object B") @unchecked) match {
    case A.f => ()
}

It can't match "Object B" against a pattern "Object A", so it throws a MatchError.

To your larger question: you can't/shouldn't override values and methods on a companion object. Polymorphism applies to classes and their instances, not to static methods or values. There's probably a better way to think about your program that doesn't involve overriding vals/defs on a companion object.

Alex Yarmula
  • 10,477
  • 5
  • 33
  • 32
  • Thanks for the clarification! I agree with you on the need for overriding companion objects in general, but the reason I want to do it here is for testing purposes, where I want to replace a method inside the companion object with another method. Also, some languages allow overriding of static methods. – deepkimo Apr 30 '13 at 21:05
1

This is interesting! If you put the class definitions into a file and compile it using scalac -print test.scala you will see something like this,

[[syntax trees at end of                   cleanup]] // scala
package <empty> {
  class A extends Object {
    def g(): String = A.f();
    def <init>(): A = {
      A.super.<init>();
      ()
    }
  };
  object A extends Object {
    private[this] val f: String = _;
    <stable> <accessor> def f(): String = A.this.f;
    def <init>(): A.type = {
      A.super.<init>();
      A.this.f = "Object A";
      ()
    }
  };
  class B extends A {
    <synthetic> private[this] val x$1: runtime.BoxedUnit = _;
    def <init>(): B = {
      B.super.<init>();
      B.this.x$1 = {
        case <synthetic> val x1: String = ("Object B": String("Object B"));
        case5(){
          if (A.f().==(x1))
            {
              val x2: String = x1;
              matchEnd4(scala.runtime.BoxedUnit.UNIT)
            }
          else
            case6()
        };
        case6(){
          matchEnd4(throw new MatchError(x1))
        };
        matchEnd4(x: runtime.BoxedUnit){
          scala.runtime.BoxedUnit.UNIT
        }
      };
      ()
    }
  }
}

This shows that the compiler creates a class B with an initialization that performs a match checking to see that the value you used in override the val A.f is equal to the original value if (A.f().==(x1)). Doesn't seem to be too useful right? If they're not equal it throws the match error by calling case6(). We can confirm this by changing the definition of the class B to override val A.f = "Object A".

class B extends A {
        override val A.f = "Object A"
}

scala> val b = new B
b: B = B@1fc6dc6

So how to fix it? You can just do this,

class B extends A {
        override def g = "Object B"
}
scala> val b = new B
b: B = B@19bdb65

scala> b.g
res1: String = Object B

or

class B extends A {
        val f = "Object B"
}
scala> val b = new B
b: B = B@1e5ed7b

scala> b.f
res0: String = Object B
tysonjh
  • 1,329
  • 1
  • 12
  • 27
  • Your fixes are a smart way to get around this simple code example, but not what I am really looking for, since I want to call the original A.f method. – deepkimo Apr 30 '13 at 21:10
  • Yes, traits are a good way to structure this, but in my case I want to test code that already exists without changing the main code. – deepkimo Apr 30 '13 at 21:13