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

Document behaviour of private modifier for top level classes #11323

Open
kynthus opened this issue Feb 5, 2021 · 9 comments
Open

Document behaviour of private modifier for top level classes #11323

kynthus opened this issue Feb 5, 2021 · 9 comments

Comments

@kynthus
Copy link

kynthus commented Feb 5, 2021

I referred to #7780.

In Scala 3, A private global class in the field or return type of a method,
I interpreted that the members themselves do not need to be kept private.

// pkg/Factory.scala

package pkg

protected class Protected // Visible from pkg or its subpackages
private   class Private   // Same as above in Scala3(???)

object Factory {

  def createProtected: Protected = new Protected()
  def createPrivate  : Private   = new Private() // Error in Scala 2, but OK in Scala 3

}

So, I think that protected and private in the global class have the same meaning.
Is this correct?

@sjrd
Copy link
Member

sjrd commented Feb 5, 2021

There's at least a difference for Scala.js: a protected thing can be exported to JavaScript (e.g., with @JSExportTopLevel) while a private thing cannot.

@kynthus
Copy link
Author

kynthus commented Feb 5, 2021

@sjrd Thank you for telling me quickly and in detail!
In other words, there is no particular change unless you use Scala.js, right?

@sjrd
Copy link
Member

sjrd commented Feb 5, 2021

Probably not, no.

@bishabosha bishabosha changed the title Difference between protected and private modifiers in global classes Document behaviour of private modifier for top level classes Feb 5, 2021
@odersky
Copy link
Contributor

odersky commented Feb 7, 2021

The error you get in Scala 2 is

i11336.scala:10: error: private class Private escapes its defining scope as part of type pkg.Private
  def createPrivate  : Private   = ???//new Private() // Error in Scala 2, but OK in Scala 3

If createPrivate is made private, the error goes away.

@kynthus
Copy link
Author

kynthus commented Feb 8, 2021

@odersky
That's right!
If the createPrivate method itself is private, you can compile it in Scala 2.
On the other hand, what surprised me was that in Scala 3 it looks like you don't have to add the private qualifier.

I wondered if this constraint was removed from private, it would be treated the same as protected.
But I got the knowledge that there is a difference in exporting Scala.js.

@odersky
Copy link
Contributor

odersky commented Feb 8, 2021

private for toplevel classes is basically the same as package private in Java except that inner packages can still see the class. Class Private in the example above is interpreted as

private[pkg] class Private

Private leakage checks are not done for package private and qualified private members. Only regular object-private members are checked. That explains the discrepancy with Scala 2. I tried to make package-private members be checked also but that causes lots of tests to fail, so it would be too risky to do.

An alternative is to change the encoding; keep private for toplevel classes intact and change it later in the pipeline to private[pkg]. But I am not sure what's gained for that. It flips the behavior of leak checking in a special case, but there's no good justification why one would want that if package private is excluded from leak checking anyway.

@odersky odersky closed this as completed Feb 8, 2021
@odersky odersky reopened this Feb 8, 2021
@odersky
Copy link
Contributor

odersky commented Feb 8, 2021

This should probably go to a different repo about spec changes.

@kynthus
Copy link
Author

kynthus commented Feb 8, 2021

@odersky Thank you for your polite explanation.
I see, it's because it's treated as a private[pkg] that isn't restricted.
Thanks also for explaining why does not impose restrictions only on private[pkg].

I learned that in Scala 3, it can change considerably to private[this] or private[pkg] depending on where private appears.
As a result, users felt that they needed to use access modifiers more carefully.

@som-snytt
Copy link
Contributor

Another design dimension is "private to empty package".

At #13114 I noticed it's possible to access empty package defs in a different file, if empty a second package has a def in the current file. However, the "foreign definition in empty package" is not visible in the named package if it is private.

a.scala

private class C

b.scala

private class X // arbitrary
package p {
class Y extends X // of course
class D extends C // only if C is not private
}

It might be useful if an unpackaged (empty-packaged) top-level def could be scoped to current file by making it private. That is more useful if it's not visible in empty package in other files. There can be no qualified private for empty package, presumably

@som-snytt som-snytt self-assigned this Oct 31, 2024
@som-snytt som-snytt removed their assignment Jan 21, 2025
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

5 participants