So in honor of the new Scala 3 release I made a valiant attempt at upgrading an app yesterday. If anyone is curious to see some notes or the stream video from this experience, it’s posted here: Notion: Twitch Stream Notes 5/14/21
I made some decent progress and the SBT migration plugin worked pretty well overall, plus or minus a few confusing bits.
But I eventually got stuck on some cross-version conflicts due to mixed _2.13 and _3.x library versions among my dependencies:
[error] Modules were resolved with conflicting cross-version suffixes in ProjectRef(uri("file:/home/worace/code/contour/scala/"), "core"):
[error] org.typelevel:cats-effect-std _2.13, _3.0.0-RC3
[error] org.scodec:scodec-bits _2.13, _3.0.0-RC3
[error] org.typelevel:literally _3.0.0-RC3, _2.13
[error] co.fs2:fs2-core _2.13, _3.0.0-RC3
...etc
Basically because I’m still relying on 2.13 versions of some packages which have not upgraded yet, I end up with mixed versions due to transitive deps, even if I’ve upgraded my own immediate deps.
I think it’s something like this:
/-- A_3
My App ---
\-- B_2.13 (CrossVersion.for3Use2_13) -- A_2.13
Now I have both A_3
and A_2.13
in the tree, which it does not like.
And I understand why this is an error, at least in the normal SBT model – it’s trying to prevent me from accidentally mixing e.g. a 2.12 and 2.13 version of some lib which are assumed to be binary incompatible.
However my intuition of the special 2.13 ↔ 3.0 binary compat case was that SBT would somehow know to let this one slide, and treat it as just any normal version eviction case, where one eventually gets picked and I can at least try to run the application. This is just an application, not a library that I would be re-publishing. So I’m not as concerned about potentially having mixed suffixes in the tree from that perspective. Just wanted to see if I could get it to run.
There’s a somewhat related note in the Scala 3 Migration Guide which says:
it is discouraged to publish a Scala 2.13 library that depends on a Scala 3 library or vice-versa. The reason is to prevent library users from ending up with two conflicting version
foo_2.13
andfoo_3
of the same foo library in their classpath.
Which is exactly what I was running into. However I hoped that the fact that my case was an app rather than a library (that I would be re-publishing) meant maybe I was somehow exempted from this. But perhaps that is not the case?
Alternatively, I suppose I could do a bunch of dep override surgery by hand to exclude the conflicting suffixes (for example in the case above I could manually exclude A from B’s transitive deps). But that sounds pretty onerous and would probably make the 2.13 compatibility feature much less useful for me in practice.
It sounds like maybe I just had an incomplete understanding of how the 2.13 ↔ 3.0 lib story would be used, but curious to see what advice anyone else has on how they are handling this for standalone apps (rather than libs).