Why is Scala losing the actual type when it is assigned to an abstract type?

106 views Asked by At

Given the following code:

abstract class Foobar {

  type Parent <: Foobar

  def parent: Option[Parent]

}

class Foo extends Foobar {

  type Parent = Nothing

  val parent = None

  // ***
  // Taking the explicit declaration of the "bar" method's return type
  // "Bar" away convinces Scala to compile the code. In other words:
  //
  //      def bar() = new Bar
  // ***
  def bar(): Bar = new Bar {

    type Parent = Foo

    val parent = Some(Foo.this)

  }

}

abstract class Bar extends Foobar

(new Foo).bar().parent.get.bar()

My questions:

  1. Can you explain why this is happening? See inline comment above.

  2. Do you have a solution for this?

I'm getting the following error message from Scala-SDK's Scala Worksheet in version 3.0.2-vfinal-20131028-1923-Typesafe:

Multiple markers at this line
  - value bar is not a member of scrap.Bar#Parent
  - value bar is not a member of scrap.Bar#Parent

OK, leaving out the "bar" method's return type declaration leaves me with the following info message from the Scala Worksheet:

> res0: scrap.Bar{type Parent = scrap.Foo; val parent: Some[scrap.Foo]} = scra
                                                  //| p$Foo$$anon$1@3bba229e

Is there a way to give this type a decent name, ideally "Foobar#Parent"?

1

There are 1 answers

1
Didier Dupont On BEST ANSWER

What happens in

(new Foo).bar().parent.get.bar()

is :

  • new Foo is Foo
  • .bar() in Foo is of type Bar (declared so, even if the actual result is more than that)
  • .parent in Bar is not redefined, neither is Parent, so it is just parent: Parent <: FooBar inherited from FooBar. The bound FooBar will be used.
  • there is no bar in FooBar, so .parent.bar() fails.

If you declare in Foo

def bar(): Bar { type Parent = Foo} = new Bar {// same as your code}

or simply

def bar() = new Bar {// same }

letting the type be inferred, it works fine.

If you declare def bar(): Bar, this is where the typing stops. Just as if you declare def bar(): Any = whatever, it will not consider the type of whatever. And a subtype of Foo would be allowed to return a Bar with a different Parent type.


Regarding your update : I 'm not sure why I would want to have that type displayed as FooBar#Parent, which tells close to nothing, but anyway, I don't think you can persuade scala to do that.

The easy way to have a simple display would be to name the type you return there rather than anonymously extend Bar. If you don't want to do that, you have at least to declare the return type of the method, which is good practice anyway. Otherwise, scala will display what it infers, with maximum precision. You can use def Bar: Bar {type Parent = Foo} = ..., or you may declare a type alias for that (preferably, outside Foo) :

type BarWithFooParent : Bar {type Parent = Foo }
def bar(): BarWithFooParent = new Bar() { … }