Backwards compatibility in scala 3 of deprecated parts of scala 2.13

At the moment, we’re on course for using the 2.13 scala stdlib for scala 3, and maintaining backwards compatibility throughout the lifecycle of scala 3.

There is a number of APIs in the scala 2.13 stdlib that are in a deprecation cycle. .mapValues returns a MapView, and was slated for removal in 2.13 + 1, after which a new eager mapValues was planned to be introduced in 2.13 + 2. Stream was planned for removal in 2.13 + 1, Traversable is planned for removal and IterableOnce for removal of all implementations in favour of the same methods on its iterator, +(String) on numeric types, Either's RightProjection, and all sorts of bits and pieces.

There is also a number of APIs that are indented to become final and are marked deprecatedInheritance, for example some annotations like BeanProperty.

Removal of these deprecated parts, or making deprecated parts final will break backwards compatibility.

What should we do with these? One option would be to not promise of backwards compatibility for parts already deprecated in scala 2.13.1 when scala 3 will have its own stdlib. Another one is to keep these parts until scala 4 comes along, but the timelines for that are pretty long.

2 Likes

I think the only realistic options are

  1. releasing a 2.14 that’s mostly a cleanup before 3.0, or
  2. going in to 3.x with a mess until 4.0 ever comes along.

/hides

Why do you think removing deprecated members somewhere in the 3.x cycle isn’t realistic?

I don’t think it’s realistic to remove deprecated members in scala 3 if it were released with a blanket backwards compatibility promise. Being up-front about backwards compatibility being limited to things that aren’t already deprecated early in 2.13.x makes it a lot more reasonable IMO.

That’s why I think it’s important to have that discussion now.

2 Likes

It means you need a cutoff point somewhere where you say e.g., everything from 3.2 onwards is not fully backwards compatible anymore with 2.13, 3.0 and 3.1. Then everything has to re-release for 3.2, build tools and users need to be aware that you can’t safely mix 3.2 with 2.13, or 3.2 with 3.1, while you could safely mix 3.1 with 3.0 or 3.1 with 2.13.

It seems more realistic IMHO to be fully backwards compatible from 3.0 (well actually from 2.13 or 2.14, but ok…) onwards and not break compatibility somewhere in the middle and have some magical 3.2 version.

It definitely is important to have that discussion before it’s too late. Or at least before it’s even later than now.

You could differentiate between a library that uses deprecated members, either directly or transitively, and one that doesn’t. Libraries that do use deprecated members would then need to adjust and re-publish when the deprecated members they are using disappear from the library.

I could think of a couple of schemes that conform to that. e.g. _3 is backwards compatible, but _3.0 is not. If you publish against any interfaces that are deprecated since 2.x, you get a _3.x artifact, otherwise you get a _3 artifact.

In theory you could introduce a new deprecation annotation specifically for “may be removed during the 3 lifecycle” for that if it would otherwise interfere too much with other deprecations, but in practice you can’t in 2.13.x because it’s not forward compatible.