-
Notifications
You must be signed in to change notification settings - Fork 12
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
[proposal] Literal Scala versions #16
base: main
Are you sure you want to change the base?
Conversation
bldPatches(2, 13, 9) | ||
bldPatches(3, 0, 2) | ||
bldPatches(3, 1, 3) | ||
bldPatches(3, 2, 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 could cause problems that this list must be maintained by hand.
I wonder if we can come up with some way that Scala Steward could recognise these versions and bump them, e.g. by using a String here instead of major,minor,patch?
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'd rather not get into a position where we have to release a new version of this library (and all downstream tooling based on it) for every new Scala version.
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.
Good call out. I tried to avoid enumerating all known versions by hand, but for the sake of to be understood by scala-steward we could create a list of all known versions and put it into a separate file:
private [scalacoptions] KnownScalaVersions {
protected val knownVersionStrings = Seq(
"2.12.0",
"2.12.1",
...
"3.2.0"
)
}
and then use it from the ScalaVersion object:
object ScalaVersion extends KnownScalaVersions {
val knownVersions = knownVersionStrings.map(fromString.unsafe)
}
or something like that.
Would it work, wdyt?
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.
Can we just leave it open ended? So unless a series is known to be EOL it will accept not-yet-released versions?
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.
@armanbilge just to make sure I understand you correctly,
you would prefer to avoid having scala-steward involved into that completely, wouldn't you?
I.e. rather than having scala-steward involved, you're suggesting to make ScalaVersion
more flexible and supporting versions like "2.13+", "2.13.9+", etc?
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.
you would prefer to avoid having scala-steward involved into that completely, wouldn't you?
Right, it's not that I care about Scala Steward specifically, but rather that we would have to release a new version of this library (and thus a new version of all the plugins that use it) for every new Scala version.
you're suggesting to make
ScalaVersion
more flexible and supporting versions like "2.13+", "2.13.9+", etc?
I don't think that's necessary. But I do think it should accept 2.13.20
today even though that's not yet released.
scalacOptions.value | ||
}, | ||
libraryDependencies ++= Seq( | ||
"org.typelevel" %%% "literally" % literallyVersion |
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.
Is there a way for us to check that literally
doesn't end up on our users' runtime classpath when depending upon this library? Perhaps I'm being paranoid 😛
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.
Hmm... I like your idea, but is it feasible in principle? I'll try to figure out that.
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 don't necessarily mean an automated way to do this but it would be nice to create a project that depends on this library and check the runtimeClasspath
in sbt 😃
import c.universe._ | ||
|
||
if (ScalaVersion.fromString(s).isEmpty) | ||
Left(s"incorrect or unsupported Scala version") |
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.
Perhaps we should use the same error message String as ScalaVersion.fromString.unsafe
here?
val gen = | ||
Arbitrary | ||
.arbitrary[String] | ||
.filterNot(Parser.genericVersionRe.pattern.matcher(_).matches()) |
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.
Not urgent, but perhaps we should have a few example-based tests in here too?
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.
Do you mean, tests for some pre-defined values like "0.1.2" , "3.4.5" and so on?
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 like this proposal, although I have not felt the pain that motivates its introduction personally.
I'm a little worried about introducing a set of "known Scala versions", and it might annoy users if they can't declare that an option should be removed in an as-yet-unknown Scala version if they are unable to upgrade scalac-options for some reason, for example if they cannot upgrade the dependent plugins like sbt-typelevel or sbt-tpolecat due to an unrelated issue.
Do you think there is some way that we could mitigate that, for example by providing an escape hatch?
I had a thought, not entirely sure if it's relevant here. .isBetween(sver"2.11.0", sver"2.13.0")) So far it seems like the plan for Scala 3 is to have LTS series e.g. Scala 3.3.x looks like to be an LTS, which will get backports of features from the active development series. So it doesn't seem impossible we might frequently end up in a situation where a compiler flag is available in e.g. 3.3.10+ and 3.5.0+ but not in 3.4.x at all. Since it's not a clean in-between relationship, do we have a good way of expressing this? I suppose it's not different than a flag that appears in the middle of 2.12 and 2.13 series. |
59ed2ce
to
fb52978
Compare
Yes actually the |
Would we consider bringing something like |
Sounds good to me. 👍 |
fb52978
to
4ec3ca4
Compare
4ec3ca4
to
40cbc47
Compare
Disclaimer: everything is controversial here! Also for now it is supposed to fail binary compatibility checks against v0.1.1. Honestly, I am not sure if it is even feasible to maintain the binary compatibility in this case. But I am open to any thoughts or suggestions! Also since it is a pretty young project, perhaps breaking the bin-compat would not be that bad for now, if it helped to make it more powerful and user-friendly.
What?
The primary goal for this PR is to add support for compile-time literals (
sver
) for referencing scala versions, e.g.:instead of
Therefore it allows to get rid of pre-defined
ScalaVersion
constants likeV2_11_0
, etc.To achieve that it makes the construction of
ScalaVersion
class safe, i.e. it becomes impossible to create aScalaVersion
instance that is not currently supported:Also it introduces runtime type-safe constructors:
and their "unsafe" counterparts:
Moreover it introduces a collection of all Scala versions that the library is currently supposed to support:
Since it is a
SortedSet
it allows range operations:Which is an additional win, I think.
Why?
Unsafe (unvalidated) models are generally discouraged in Scala, especially in common-purpose libraries. However, simply adding safe
from
/fromString
runtime-validated methods would make the library inconvenient for users: I would suppose that users would appreciate the ability to refer to some particular Scala version without extra hustle of checking results every time they need it. On the other hand, maintaining a huge bunch of all supported Scala version in a form ofV2_11_0
,V2_11_1
, ...,V3_2_0
, etc is kinda ugly and (a little bit) error-prone.So now the idea of some macro-based solution emerges!
How?
The literally library seems to be pretty suitable for that: it allows to turn strings into literals of some specific type and the Scala versions are naturally represented as strings. So these two concepts match each other pretty well.
Unfortunately, it seems impossible to define a macro and use it within the same compilation module. Therefore the macros for the literals have to be extracted into a separate module
scalac-options-macros
. Furthermore, sinceScalacVersion
class itself is used by the macro, it has to be extracted out of the main module as well.Hence there are two additional modules added:
scala-options-macros
andscala-options-core
. The latter is proposed to host core classes likeScalaVersion
(along withScalaOption
and maybe others in the future), whereas the primaryscalac-options
should keep the DSL definitions only (like constants from theScalacOptions
object).Nevertheless, the new
scala-options-macros
andscala-options-core
are made unpublishable. That means, they exist for the sake of compile-time separation only. Whereas the primaryscalac-options
module gets all the content from those two modules and hence looks like a "solid" single module, when published (see Macro-Projects).What Else?
There's one more module introduced:
scalac-options-testkit
. For now it hosts Scalacheck'sGen
andArbitrary
forScalaVersion
only which are also made used in some. Therefore the module'smain
config contains Sclalacheck instances, whereas thetest
config hosts all the unit tests. I think it may be pretty convenient anyway.