-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Deprecate package object with extends clause #7662
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
Conversation
|
Based on the discussion in scala/scala-dev#441 this seems to be a pretty controversial change, are you intending to go ahead with this anyway ? |
|
Yep |
|
Seriously, though, I read that discussion as having valid pros and cons for deprecation, though with the pros outnumbering the cons. |
smarter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read that discussion as having valid pros and cons for deprecation, though with the pros outnumbering the cons.
Please list these pros/cons in the commit message :).
|
will do -- in the mean time, here's a beauty: 357905c |
7973505 to
ae1c98f
Compare
|
How does object `package` extends Cbehave, instead? |
ae1c98f to
67dd905
Compare
5e9acc1 to
9ee9469
Compare
9ee9469 to
acc9997
Compare
|
The best part is that it blows the future wide open to re-introduce package objects as a module system. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. People want to take away our cycles, but where would we be without cycle sheds?
| val tstart = in.offset | ||
| atPos(start, if (name == nme.ERROR) start else nameOffset) { | ||
| val template = templateOpt(mods, name, NoMods, Nil, tstart) | ||
| val template = templateOpt(mods, if (isPackageObject) nme.PACKAGEkw else name, NoMods, Nil, tstart) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scalafmt where are you? SIP-12 where are you? There's always either too many spaces or not enough spaces around a paren. if ( isPO ) ofc. Also ofc we need API for name.orElsePackageIf(b). But I'd be satisfied with deleting one space, if my new computer glasses worked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but! who doesn't like more space! (hint: markdown)
|
|
||
| // we can't easily check this later, because `gen.mkParents` adds the default AnyRef parent, and we need to warn based on what the user wrote | ||
| if (name == nme.PACKAGEkw && parents.nonEmpty) deprecationWarning(tstart, s"package object inheritance is deprecated", "2.13.0") | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a pity it's quite pithy. I wonder if it should say another word, like, "And we're taking package objects away from you entirely in the next release." First they took away my inheritance, then my objects, next there will be no packages at all under the tree. No fun living at the orphanage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I blame the boomers. Anyway, I added some links and verbiage.
To enable migration to top-level definitions as a replacement
for package objects, we must first remove the main aspect of
package objects that complicates their implementation and
that would not longer be supported by top-level definitions.
Once that's done, the migration would mean rewriting
`package object foo { ... }` to `package foo { ... }`,
assuming the `...` are exclusively definitions, and not statements.
Losing inheritance for package objects means that some code duplication
will be required (you'll have to add val/type aliases for members,
instead of inheriting the members directly). You also can't use inheritance
to prioritize implicits, but, implicits defined in packages are also
being phased out (i.e., package prefixe don't contribute to the implicit scope).
The main complication with supporting full-on package objects is
that name resolution needs to know all definitions in a package
in order to resolve the parents of said package, and when there's
a cycle there (a package object inherits a class defined in the package),
the implementation becomes challenging due to the open nature of packages.
While we can easily rule out the cycle in the following snippet that
uses plain objects, with package objects it's not so easy:
```
object Dogs extends Dogs.Animal { // cycle!
class Animal
}
```
The spec currently has similar awkwardness in place to deal with this:
> The package object should not define a member with the same name as
> one of the top-level objects or classes defined in package $p$. If
> there is a name conflict, the behavior of the program is currently
> undefined. It is expected that this restriction will be lifted in a
> future version of Scala.
Finally, package objects are objects in name only -- they already are
not first-class values, and now they also can't extend anything. This
leaves them as simply syntactic baggage. Top-level definitions are
intuitive, require no syntax, and can be implemented in the same way
as package objects behind the scenes, albeit without the issue of cycles.
```
package object foo
class D {
println(foo) // error: package foo is not a value
}
```
acc9997 to
3e18e9f
Compare
|
It's a big change that will break a lot of libraries. Shouldn't this go through a SIP? |
|
The actual change will. Deprecation won't. |
|
It's nearly the same thing, though – I doubt it would be 'undeprecated' if the SIP doesn't go through. Seems like a very one-sided decision to me. |
I tend to agree. A mitigation can be if there was a warning suppression mechanism for the library to use so it can use fatal warnings. Deprecating is a way to say "please don't do this/this is not recommended". That is fine, but we need away to reply to the compiler "I agree, but shut up please". |
|
Sure, I started working on that here |
|
@adriaanm Can you please schedule a 2.13 community-build for this PR? |
|
Since it's merged, the community build will pick it up. I assume you're worried about its effect on warnings, but those won't show up in the community build because we already disable |
|
@adriaanm from your reply on the corresponding issue
But it's not just implicits, you may have non-implicit methods, vals, objects, etc. that you want in scope at the package level. Having to convert This is going to wreak serious havok, every code base built with |
|
They can drop fatal warnings or resolve the deprecation warnings. The compiler development can't be bound by the constraints of fatal warnings. |
|
Besides using Note that (Edit: now I wish it were called, |
Well, the issue is that there's no viable replacement. Top-level definitions aren't yet supported; custom predef is globally scoped (IDE support is probably amazing); plain objects require an import in every source file. I can appreciate that this change will make life easier for the compiler team, but it's strictly worse for users that have benefited for years from |
|
Another alternative is writing the forwarders in the package object. But I agree with you those are the tradeoffs for users making use of this feature which is being deprecated. |
|
@godenji thanks for speaking up with your concerns about this, I think the questions you are asking and the points you're bringing up will be of concern to others as well.
well, I think everyone on the SIP committee is aware of these changes, and IIRC this has come up in several meetings... ...but on the other hand, I was a bit surprised not to find top level definitions on the list at scala/scala3#5221, and I've now said so at scala/scala3#5221 (comment)
Adriaan and I both agree with @dwijnand that compiler development and the progress of the language can't be bound by the constraints of fatal warnings. https://github.com/ghik/silencer provides a escape hatch. another possible escape hatch is to enable fatal warnings only on the main Scala version you are targeting (e.g., 2.12) trying to cross-compile to multiple Scala versions without any warnings at all in any of the versions you are targeting may sometimes be possible for some projects, but making it possible in general isn't really a goal for us, it's far too constraining.
good point. @adriaanm is there a reason this deprecation couldn't wait for 2.14? are we deprecating in 2.13 because we want to remove it in 2.14 because there's something in the 2.14 roadmap that conflicts?I'd like to understand this as well. |
ah, just found scala/scala-dev#441 (comment) where I wrote:
unfortunately it seems that the minutes from that meeting were never published; at least, they're missing from https://github.com/scala/docs.scala-lang/tree/master/_sips/minutes (probably a casualty of the changeover in the SIP process lead position, I suppose), but there is a video at https://www.youtube.com/watch?v=dFEkS71rbW8 and the agenda includes "Remove package objects", with a link to scala/scala-dev#441 so essentially scala/scala-dev#441 is the SIP, so it was up for public discussion, and the committee was made aware as well so process-wise, I think once this is added to the list at |
|
Top level definitions provide no means for inheritance, which is why we’re
deprecating this aspect of package objects. That’s why we’re deprecating
now so that we can hopefully add top level definitions in 2.14
…On Wed, 20 Feb 2019 at 12:25, Seth Tisue ***@***.***> wrote:
@godenji <https://github.com/godenji> thanks for speaking up with your
concerns about this, I think the questions you are asking and the points
you're bringing up will be of concern to others as well.
no SIP??
well, I think everyone on the SIP committee is aware of these changes, and
IIRC this has come up in several meetings...
...but on the other hand, I was a bit surprised not to find top level
definitions on the list at scala/scala3#5221
<scala/scala3#5221>, and I've now said so at scala/scala3#5221
(comment)
<scala/scala3#5221 (comment)>
every code base built with -Xfatal-warnings is going to blow up.
Adriaan and I both agree with @dwijnand <https://github.com/dwijnand>
that compiler development and the progress of the language can't be bound
by the constraints of fatal warnings.
https://github.com/ghik/silencer provides a escape hatch. another
possible escape hatch is to enable fatal warnings only on the main Scala
version you are targeting (e.g., 2.12)
trying to cross-compile to multiple Scala versions without any warnings at
all in any of the versions you are targeting may sometimes be possible for
some projects, but making it possible in general isn't really a goal for
us, it's far too constraining.
Well, the issue is that there's no viable replacement. Top-level
definitions aren't yet supported
good point. @adriaanm <https://github.com/adriaanm> is there a reason
this deprecation couldn't wait for 2.14? are we deprecating in 2.13 because
we want to remove it in 2.14 because there's something in the 2.14 roadmap
that conflicts?I'd like to understand this as well.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#7662 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAFjy3S7r7QYAj6p_la5mBNgvIGjKPEOks5vPa84gaJpZM4aFOW->
.
|
|
Dotty right now has both package objects with inheritance and top-level objects, so from what I can see there's no urgency to deprecate this. |
|
There doesn't seem to be any logical reason why package objects' inheritance has to be deprecated in order for top-level definitions to go in in 2.14--or now, for that matter. Even if you use the package object mechanism for TLDs, you just need to mangle names slightly differently and the two can coexist. Yes, there's weird stuff about how the two can look into each other, but you just make some ad-hoc rules to simplify the logic during the period where both coexist. (E.g. allow the visibility to go only one way.) That the deprecation is going in before the replacement is ready is rather disappointing. That the envisioned replacement isn't actually a full replacement because it doesn't cleanly solve the implicit priority thing (as far as I can tell) is also disappointing. I understand the inherent problems that package objects have, and look forward to them going away. I haven't liked to have to use that mechanism, and I haven't enjoyed the bugs. But they're here now and work and aid usability. So without a good alternative for inheritance use cases, the deprecation doesn't feel ready to me. |
|
I, for one, am NOT looking forward to package objects going away. The fact that top-level is first-class and composable by mixins like any other object is one of the beauties of Scala to me. |
There's lots of wonderfully written detail about that "weird stuff" in gkossakowski/kentuckymule#6 (comment). |
please use scala/scala-dev#441 for any general discussion, so we can keep the discussion here on the pull request focused on 1) the implementation, and 2) the timing of the deprecation |
|
Again, please note that we’re only talking about deprecating the EXTENDS
clause of a package object. Not package objects themselves. Once your code
base is free of package objects that use inheritance, migrating to
toplevel definitions is trivial.
…On Wed, 20 Feb 2019 at 14:07, Seth Tisue ***@***.***> wrote:
I, for one, am NOT looking forward to package objects going away
please use scala/scala-dev#441
<scala/scala-dev#441> for any general
discussion, so we can keep the discussion here focused on 1) the
implementation, and 2) the timing of the deprecation
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#7662 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAFjy84d22To57SXwJKtPzVd_pz_vR1lks5vPccUgaJpZM4aFOW->
.
|
@SethTisue is it possible to run a community build that does not disable |
I think that would give less information than the current setup gives, not more. if we enable I think what you want is just to analyze the log of the existing community build and see how often a specific warning we are interested in (such as this one) crops up. I guess we'd want to make sure |
|
@dwijnand - Despite the problems, people who have used inheritance with package objects have already made as much peace as there is to be made with those issues. So it still seems plausible that the deprecation could wait until there was something with fewer rough edges to migrate to. @SethTisue - Getting that deprecation log would be great. I know I use the package object extends pattern a number of times in my code (entirely for implicits, not API exporting), but I can't tell whether I'm an anomaly in that regard. |
|
New development: #7857 |
UPDATE: the deprecation ended up being put behind
-Xsource:2.14(see #7857)Fix scala/scala-dev#441
Workaround: use a regular object and import it. I consider the explicit import an advantage: it makes it more clear where implicits are coming from. In general, I would not recommend defining implicits in package objects.
Longer term, package objects will be replaced by top-level definitions.
To enable this migration to top-level definitions as a replacement
for package objects, we must first remove the main aspect of
package objects that complicates their implementation and
that would not longer be supported by top-level definitions.
Once that's done, the migration would mean rewriting
package object foo { ... }topackage foo { ... },assuming the
...are exclusively definitions, and not statements.Losing inheritance for package objects means that some code duplication
will be required (you'll have to add val/type aliases for members,
instead of inheriting the members directly). You also can't use inheritance
to prioritize implicits, but, implicits defined in packages are also
being phased out (i.e., package prefixe don't contribute to the implicit scope).
The main complication with supporting full-on package objects is
that name resolution needs to know all definitions in a package
in order to resolve the parents of said package, and when there's
a cycle there (a package object inherits a class defined in the package),
the implementation becomes challenging due to the open nature of packages.
While we can easily rule out the cycle in the following snippet that
uses plain objects, with package objects it's not so easy:
The spec currently has similar awkwardness in place to deal with this:
Finally, package objects are objects in name only -- they already are
not first-class values, and now they also can't extend anything. This
leaves them as simply syntactic baggage. Top-level definitions are
intuitive, require no syntax, and can be implemented in the same way
as package objects behind the scenes, albeit without the issue of cycles.