15

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dima
  • 39,570
  • 6
  • 44
  • 70
  • http://stackoverflow.com/questions/19237049/how-to-call-super-method-when-overriding-a-method-through-a-trait – dmitry Apr 30 '16 at 09:58
  • @dmitry that does not answer my question: the answer there basically changes the design so that both the class and the trait extend a common parent, that defines the method being overridden. This is trivial, and not what I am after. – Dima Apr 30 '16 at 11:52
  • Well, here they say almost the same: http://stackoverflow.com/questions/6849626/calling-a-method-on-the-superclass-in-a-self-typed-trait-in-scala – dmitry Apr 30 '16 at 12:05
  • @dmitry no, they don't. You are missing the point: that answer trivializes the problem again by making `OtherStuff` extend `Foo`. The question is about overriding a method in the _self type_, not in _superclass_. – Dima Apr 30 '16 at 12:39
  • I see that, @Dima. But it may mean that direct way to achieve what you want does not exist, and not trivializing. – dmitry Apr 30 '16 at 12:44
  • 1
    @dmitry well, that's exactly the question, whether that way exists ... It just seems odd that one is allowed to override a method, but not to reference the original version. – Dima Apr 30 '16 at 21:08

3 Answers3

5

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.

dkolmakov
  • 637
  • 5
  • 12
0

I am not aware of any particular syntax to disentangle the base class and the mixed-in trait. There is, however, an easy solution to achieve the result manually by distinguishing the overridden method from the default implementation in the base class:

class Foo { def foo = defaultFoo; def defaultFoo = "foo" }
trait Bar { self: Foo => override def foo = self.defaultFoo + "bar" }

As expected

new Foo with Bar foo == "foobar"
new Foo foo == "foo"
DanielM
  • 1,023
  • 8
  • 18
  • Well, if I could change `Foo`, there would be a bunch of possibilities available (starting with the obvious - make it extend a super-trait, common with `Bar`). The problem arises when once needs to do it without modifying `Foo`. – Dima May 13 '16 at 10:22
  • I see, so this is the root of your problem in fact. Sorry for not catching that. Is it a requirement that `Foo with Bar` has exactly the same interface as `Foo`? – DanielM May 13 '16 at 10:39
  • Yeah ... :) The idea is to "pimp" `Foo` with `Bar` transparently to the downstream code, that will still be using it as just `Foo` – Dima May 13 '16 at 11:27
0

You make your trait extend Foo instead of using the self type:

class Foo {def foo = "foo"}
trait Bar extends Foo {
  override def foo = super.foo + "bar"
}
new Foo with Bar foo // barfoo

See also this answer.

Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • Well, if I made it extend Foo, that would make it a different problem, wouldn't it? I mean, it would have nothing to do with calling an overridden method fro self-type, which is what I was asking about ... – Dima May 23 '16 at 10:18
  • Well, if I were alibistic, I could propose declaring `Foo` as the self type for `Bar` in addition to inheriting it. Then you would technically be calling an overriden method from self type ;) – Mifeet May 23 '16 at 11:20
  • But the main point was to solve your declared problem: "The idea is to 'pimp' Foo with Bar transparently to the downstream code, that will still be using it as just Foo". – Mifeet May 23 '16 at 11:22
  • I just wonder, do you really think I did not know about being able to extend `Foo` :) Obviously, that is NOT "the main point" ... – Dima May 23 '16 at 12:19
  • 2
    (1) I had no idea, you know how it works on SO..., (2) it was the main point of my answer, (3) sorry I don't have a better answer other than Daniel's, I'll still leave my answer here for future reference – Mifeet May 23 '16 at 14:00