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

Final, Anonymous, or singleton (objects) classes should not have protected non-overridden members #11434

Closed
diesalbla opened this issue Mar 14, 2019 · 6 comments

Comments

@diesalbla
Copy link

Note: This is not a bug, it's a feature (request)

Consider the following code snippet, which compiles under Scala 2.12:

trait A 
trait B { protected x: Int }
final class C extends A with B {
  protected x: Int = ""     // OK
  protected y: String = ""  // KO
}
object D extends A with B {
  protected x: Int = 42      // OK 
  protected y: String = ""   // KO 
}
val e = new A with B {
  protected x: Int = 42     // OK
  protected y: String = ""  // KO 
}

The protected access modifier lies between public and private: it grants access to the member for the subclasses, but denies it to any other class. However, since final classes, cannot be extended, and have no sub-classes, it makes no sense for them to declare a protected member. Unless, of course, the said protected member was declared in a superclass, and so its access cannot be restricted to private.

Nevertheless, one can still desire for the compiler enforce the condition that a final class does not declare non-inherited protected members, either by:

  • Requiring that all protected methods also be marked as overridden, in which case the compiler would detect if they are not declared in a super-type; or
  • By adding to the compiler a check for this condition on final-like classes.

Note that here, super-class also includes traits or classes, and final classes includes anonymous classes and singleton classes.

@sjrd
Copy link
Member

sjrd commented Mar 14, 2019

There are reasonable use cases in Scala.js for protected members in final classes, because they are visible to JavaScript (or can be exported) but not to other Scala code.

Please don't make this idiom invalid.

@diesalbla
Copy link
Author

diesalbla commented Mar 14, 2019

There are reasonable use cases in Scala.js for protected members in final classes, because they are visible to JavaScript (or can be exported) but not to other Scala code.

Then why not make them public in that case? Subclasses can extend access grants on inherited protected members to make them public.

trait A { protected def x: Int = 0 }
trait B extends A { override def x: Int = 0} // x is now public  

Perhaps It could help to better understand the use cases you are referring to, if there was a small example of those, and why they would need protected members in final classes.

@joroKr21
Copy link
Member

Sometimes I use protected in object as a workaround for linter limitations:

case class Foo(data: Map[String, Long])
object Foo {
  protected implicit val encodeData: Encoder[Map[String, Long]] = ...
  implicit val encoder: Encoder[Foo] = deriveEncoder // -> a macro
}

If we make encodeData private linters would complain that it's not used.
But it is after macro expansion!

@sjrd
Copy link
Member

sjrd commented Mar 14, 2019

@diesalbla Here is a typical usecase:

final class C {
  // For Scala users, give a List[Int]
  def xs: List[Int] = ???

  // For JS callers, give a js.Array[Int]
  @JSExport("xs")
  protected def jsXs: js.Array[Int] = xs.toJSArray
}

@hrhino
Copy link

hrhino commented Mar 15, 2019

Scala is full of silly things you can do which one might think they want to be a compiler error. However, we also have scalafix, which can be made to emit that exact error. Moreover, while I would never have guessed that there would be a reason to use protected over private here, Sébastien reminds me that just because I can't think of it doesn't mean it shouldn't exist.

So I firmly think this should not be implemented.

@diesalbla
Copy link
Author

@joroKr21 Yeah, I have been bitten by such errors. I would say, though, that it is the linter that should be adjusted to the language, or disabled, not the other way around.

There are reasonable use cases in Scala.js for protected members in final classes, because they are visible to JavaScript (or can be exported) but not to other Scala code.

@sjrd Saw the use case. So, you use a modifier keyword with one meaning (to share with inheritors only) for a very different one (share with anyone in JavaScript). I do appreciate that porting Scala to another platform is difficult, but one may have wished for a cleaner way.

En fin...

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

4 participants