Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to call an overridden method from self type?

Tags:

scala

Consider this:

 class Foo { def foo = "foo" }
 trait Bar { self: Foo =>
    override def foo = "bar"
 }

I was pleasantly surprised to find out that this is possible, and works as expected:

new Foo with Bar foo 

returns "bar". The question is whether it is possible for Bar.foo to invoke Foo.foo, like one would often do in the "ordinary" inheritance case. override def foo = super.foo + "bar" does not work (says "foo is not a member of AnyRef), and neither does override def foo = self.foo + "bar" (it ends up just calling itself, and results in infinite recursion). I tried a few other combinations (like self.Foo.foo, Foo.this.foo etc.), but without any luck.

Is this just impossible?

like image 289
Dima Avatar asked Apr 29 '16 18:04

Dima


1 Answers

No. It is impossible to call overridden method from a self type.

Firstly the trait Bar is not a successor of class Foo so it is not possible using super.foo.

And secondly it is also not possible using self.foo since self is actually of type Bar with Foo. It can be shown by printing the program after typer:

$ scalac -Xprint:typer test.scala
[[syntax trees at end of                     typer]] // test.scala
package <empty> {
  class Foo extends scala.AnyRef {
    def <init>(): Foo = {
      Foo.super.<init>();
      ()
    };
    def foo: String = "foo"
  };
  abstract trait Bar extends scala.AnyRef { self: Bar with Foo => 
    def /*Bar*/$init$(): Unit = {
      ()
    };
    override def foo: String = "bar"
  };
  class FooBar extends Foo with Bar {
    def <init>(): FooBar = {
      FooBar.super.<init>();
      ()
    }
  };
  object TestApp extends scala.AnyRef {
    def <init>(): TestApp.type = {
      TestApp.super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      val a: FooBar = new FooBar();
      scala.this.Predef.println(a.foo)
    }
  }
}

So with self.foo you are trying to access the method foo of the trait Bar. Such behavior matches the Scala Specification (PDF):

The sequence of template statements may be prefixed with a formal parameter definition and an arrow, e.g. x =>, or x: T =>. If a formal parameter is given, it can be used as an alias for the reference this throughout the body of the template. If the formal parameter comes with a type T, this definition affects the self type S of the underlying class or object as follows: Let C be the type of the class or trait or object defining the template. If a type T is given for the formal self parameter, S is the greatest lower bound of T and C. If no type T is given, S is just C. Inside the template, the type of this is assumed to be S.

It is possible to access the method using reflection but I think that it is not what you are looking for.

like image 185
dkolmakov Avatar answered Sep 20 '22 10:09

dkolmakov