-
Notifications
You must be signed in to change notification settings - Fork 407
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
Deprecate Prop.BooleanOperators in anticipation of Dotty, add scalafix rewrite rule to rewrite usages #498
Conversation
For a Boolean b and a prop p, the following is ambiguous in dotty: import Prop._ b ==> p Because Prop contains: implicit def BooleanOperators(b: => Boolean) = new ExtendedBoolean(b) implicit def propBoolean(b: Boolean): Prop = Prop(b) And both ExtendedBoolean and Prop provide a `==>` method, the one in `ExtendedBoolean` just forwards to the one in `Prop`. This is not ambiguous in scalac because `Boolean` wins against `=> Boolean` but it is in dotty (and intentionally so). In general, it seems that all the methods in BooleanOperators are also available on Prop, so BooleanOperators does not serve any purpose and can be deprecated. We can then make it non-implicit in a subsequent release of scalacheck (thus breaking source-compatiblity but not binary-compatiblity) to finally be able to compile it with Dotty (which also requires getting typelevel#423 in).
@smarter My proposal is to wait on adding any new deprecations until we get a new release out (since it's been so long). Then we can merge this (and any other PRs that deprecate parts of the API) and prepare for future API changes and/or breakage. |
So, the issue is that we cannot cross-compile scalacheck with Dotty until after a scalacheck with this deprecation in is released, and we'll then need to wait another release to be able to publish scalacheck against Dotty. So, the quicker we get this in, the better. It should be easy to make a scalafix rewrite rule for this, which mean we can get scala-steward to automatically upgrade everyone's code (https://github.com/fthomas/scala-steward/blob/master/docs/scalafix-migrations.md), so the cost should be minimal. |
ca15a4b
to
e722078
Compare
Just wrote my first scalafix rule for this :). I'll make a PR against scala-steward once this is in (in fact, I wonder if we could just ask scala-steward to run the migration rule now even if the method isn't deprecated yet, would that be possible @fthomas ?) |
e722078
to
a12f0ef
Compare
To run it: scalafix --rules=github:smarter/scalacheck/v1_14_1?sha=deprecate-BooleanOperators See https://scalacenter.github.io/scalafix/docs/developers/tutorial.html#run-the-rule-from-source-code for more information.
a12f0ef
to
4735d09
Compare
@smarter Got it. I think the reasoning makes sense, especially since it already has to be explicitly imported to be used (i.e. it's very likely the rewrite would fix all warnings). |
@smarter Scala Steward runs any rule which is configured for the bump to 1.14.1. There is no requirement that rules need to replace deprecated symbols. The rule included here seems to be a perfect candidate for being added to Scala Steward. |
I'm pretty sure dropping BooleanOperators will be a significant breaking change, even in a 2.0 release. I'm hoping there's a different workaround. Though, I haven't had time to investigative this closely, yet. |
It doesn't need to be removed, just made non-implicit to be able to compile scalacheck with dotty. |
@ashawley My understanding is that the process will be:
What do you think? Does that make sense? Anything I'm missing @smarter ? |
Oh, I thought it was going to be deleted. If it's not implicit, is it still possible to use the fencing ( |
@ashawley I'm fairly sure that the (EDIT: In fact this PR has code that demonstrates that that works.) |
@non Yes that's exactly what I'm proposing :) |
I'm 👍 on this but I'll wait until @ashawley is on board to merge it. |
Sorry, I wasn't aware that Why not deprecate and later drop I know you spent a bunch of time on this, @smarter, even creating a scalafix recipe. Maybe we can close this? I am happy to create a new PR that deprecates |
@ashawley I think that |
Yes, that's my sense what it does, as well. However, it seems like it's the same consequence, no? I've tried to verify it in #504, but Travis failures may make that difficult. |
I'm personally against anything that increases the timeline until scalacheck cross-compiles with Dotty, so I'm 👎 for #504 which would require waiting until scalacheck breaks bin-compat (by the way, is there really a pressing need to break scalacheck bin-compat again? It would have a ripple effect on the whole ecosystem, which still hasn't completed upgrading to the previous bin-compat breaking release).
A binary breakage is not an easy route by any mean, on the other hand, the combination of scalafix and scala-steward mean that source breakages aren't as bad as they were before, this is especially the case here where we're giving users a heads-up by first deprecating before breaking source-compatibility. |
I've experimented with it by publishing a jar locally with the implicit keyword for I don't think this is what binary compatibility is supposed to look like. Is it? |
This is why this PR doesn't remove the keyword immediately and instead just mark BoolenOperators as deprecated to give people a chance to fix their code before we mark it as non-implicit.
I'm not sure what you mean by that, binary compatibility does not imply source compatibility. |
@ashawley I think the idea is that if you've already compiled code into a runnable jar, you can still run the jar against the version without the The fact that you can't compile it is an indicator that source compatibility has been broken, but binary compatibility should remain. |
I follow. It just seems like a distinction without a difference. I suppose ScalaCheck is fundamentally a source-based library, so binary compatibility isn't a useful measure. |
I think the big issue is transitive dependencies. If I'm using ScalaCheck myself I need to fix my code to compile (which I have the power to do). But if I'm using Discipline, ScalaTest, or other libraries that use ScalaCheck (so I have transitive dependencies) a binary incompatible upgrade will break those libraries until they update. And if some of them update and others don't, my build will be broken (I'll be unable to use them together) until they sort it out. The options (for a future Dotty-compatible release) are:
|
In my project at work we definitely return booleans as props. I know it's not necessarily the way scalacheck is supposed to be used (i.e. assertion style) but I wouldn't be surprised if many users do that. So 👍 for deprecating boolean ops and keeping the implicit conversion. It could be moved to the |
If someone uses If instead an implicit was being dropped on some hypothetical ScalaCheck internal that was only used by downstream libraries with a dependency, I guess we could pull this off, but it would seem I'm just trying to study the options and their impact. I know it's not popular to raise these concerns, but it seems like past breaking changes have frustrated users in the past. Scalafix recipes, deprecation messages and release notes are too easily missed. |
I think classpath hell is a much bigger issue than changing an import. That's why people insist so much on binary compatibility. I guess the problem with scalacheck was that minor versions were not binary compatible, which is unexpected. |
scala-steward PR: scala-steward-org/scala-steward#853 |
Following-up on Dotty support, two things:
Add the following to your sbt: resolvers +=
"Sonatype OSS Snapshots" at
"https://oss.sonatype.org/content/repositories/snapshots" Use this snapshot of ScalaCheck for Dotty artifacts: libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.15.0-09a2431-SNAPSHOT" % "test" |
For a Boolean b and a prop p, the following is ambiguous in dotty:
Because Prop contains:
And both ExtendedBoolean and Prop provide a
==>
method, the one inExtendedBoolean
just forwards to the one inProp
. This is notambiguous in scalac because
Boolean
wins against=> Boolean
but itis in dotty (and intentionally so).
In general, it seems that all the methods in BooleanOperators are also
available on Prop, so BooleanOperators does not serve any purpose and
can be deprecated. We can then make it non-implicit in a subsequent
release of scalacheck (thus breaking source-compatiblity but not
binary-compatiblity) to finally be able to compile it with Dotty (which
also requires getting #423 in).