Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return type inference should use type of overridden method (rather than infer locally from overridden's rhs) #8159

Closed
scabug opened this issue Jan 17, 2014 · 13 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Jan 17, 2014

Given the following code:

    object SomeOddThing extends App {
      val c: C = new C
      val a: A = c
      val b: B = c
      // these will all print None
      println(a.x)
      println(b.x)
      println(c.x)
    }

    abstract class A {
      def x: Option[String] = None
    }

    abstract class B extends A {
      override def x: Option[String] = Some("xyz")
    }

    // this shouldn't compile!
    class C extends B {
      // x now has type None.type instead of Option[String]
      override def x = None
    }

    trait T {
      this: B =>
      override def x = Some("ttt")
    }

    // and this won't compile with the following error
    // error: overriding method x in class C of type => None.type;
    //  method x in trait T of type => Some[String] has incompatible type
    // class D extends C with T {}
    //       ^
    class D extends C with T {}

The compiler seems to infer the type of C.x as None.type so D doesn't compile.

@scabug
Copy link
Author

scabug commented Jan 17, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8159?orig=1
Reporter: Giovanni Botta (giodude)
Affected Versions: 2.10.3
See #7212, #7873

@scabug
Copy link
Author

scabug commented Jan 18, 2014

@som-snytt said (edited on Jan 18, 2014 4:51:05 AM UTC):
https://groups.google.com/forum/#!topic/scala-internals/6vemF4hOA9A

I thought I saw a ticket after that. (#7212)

@scabug
Copy link
Author

scabug commented Jan 18, 2014

@som-snytt said:
The issue should be an improvement since, according to SO, "The spec 4.6.4 only promises to infer a conforming type for the result type of the overriding method."

http://stackoverflow.com/a/21198284/1296806

The distinction is between "Scala is buggy" and "Scala is greatly improved."

@scabug
Copy link
Author

scabug commented Jan 18, 2014

Giovanni Botta (giodude) said:
I still think this is a bug though: how is None.type conforming to Option[] and how is it possible to violate the original contract of method x like that? I guess I don't quite understand what's happening here.

@scabug
Copy link
Author

scabug commented Jan 20, 2014

@adriaanm said:
No, the inference is correct. You're allowed to refine a method's result type covariantly (a method in a subtype may have a result type that's "stricter"/a subtype of the result type of the method in the supertype). None.type <: Option[Nothing] <: Option[String] (because case object None extends Option[Nothing] and Option is covariant in its type parameter).

@scabug
Copy link
Author

scabug commented Jan 20, 2014

@adriaanm said:
As a general rule, don't let type inference influence your non-private interface.

@scabug
Copy link
Author

scabug commented Jan 21, 2014

Giovanni Botta (giodude) said:
I finally get it. I wasn't thinking about the fact that the return type can change covariantly. What should the rule be though? Should a subtype method have as return type the first explicitly declared type in the class hierarchy? Meaning, the following should be possible:

class A{
  def x: Option[String] = None
}

class B extends A{
  override def x: None = None
}

// should NOT compile
class C extends B{
  override def x = Some("123")
}

class D extends A{
  override def x = None
}

// should compile
class E extends D{
  override def x = Some("123")
}

Am I right?

@scabug
Copy link
Author

scabug commented Jan 21, 2014

@som-snytt said:
Oh good, then you can accept my SO answer. Probably the extended discussion should be on ML or SO, but I think the title says it, if there's going to be a rule to guide inference, use what you're overriding. "the first explicitly declared type" isn't a thing. The rule for us is, if you're designing for extension, always annotate.

But does the inference guide kick in only on explicit override, or also for impl of abstract member?

@scabug
Copy link
Author

scabug commented Jan 21, 2014

Giovanni Botta (giodude) said:
So to your point if class C was originally designed for inheritance, should I assume that not specifying the return type for method x in class C is a bug?
That actually makes sense to me. I'll file a bug to the original authors of the code.

@scabug
Copy link
Author

scabug commented Jan 21, 2014

@adriaanm said:
Yes, I would consider a public method without a type signature a bug (or at least very smelly code) in most cases.

Regarding to your first question, neither C nor E should compile (even after you correct B to use None.type for x's return type)
None's type is None.type, and as it's a singleton type, the only value to have that type is None itself.

@scabug
Copy link
Author

scabug commented Jan 22, 2014

@som-snytt said:
I think the first question was s/should/ought, if the proposed rule was in effect and made it work intuitively.

But the Scala way is to have intuitive rules with unintuitive edge cases, not the other way around! Otherwise, there would be no puzzlers.

@scabug
Copy link
Author

scabug commented Feb 19, 2014

@som-snytt said:
The reason I thought I saw a ticket is that I created one: #7873

@scabug
Copy link
Author

scabug commented Feb 19, 2014

@adriaanm said:
See #7212

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants