Tools for cross-building code with 2.13 / varargs overloading compatibility woes in 2.13

We are currently trying to compile Akka / Akka Http and then dependent projects with Scala 2.13.0-M5. We found that one obstacle in Akka HTTP is that we have overloads of this form in various public APIs:

def create(els: HttpHeader*) = ???
def create(els: immutable.Seq[HttpHeader]) = ???

That used to work before because the varargs type HttpHeader* was implemented as Seq[HttpHeader] so both definitions had distinct signatures. In 2.13, both definitions have the same signature and cannot live next to each other.

We know that having these overloads in the first place is not a good idea because the overloading resolution was somewhat non-obvious even before. In the long-term we’d try to migrate to a different overloads, probably using the more common pattern of

def create() = ??? // if needed at all
def create(first: HttpHeader, rest: HttpHeader*) = ???
def create(els: immutable.Seq[HttpHeader]) = ???

We are now thinking about our options how to cross-compile code for 2.11, 2.12, and 2.13 that doesn’t require us to fork parts of our code base for 2.13 (e.g. using scala-2.12 source dirs with sbt).

The clever idea currently proposed by @raboof is to use a type alias instead of immutable.Seq[] for one of the overloads that can be chosen differently for 2.12 and 2.13 so that both definitions still disambiguate. The downside is that this introduces a new uncommon type into our code base in various places that we won’t be able to get rid off until support for 2.12 would be dropped.

As an alternative, we started thinking about using either macro annotations or a compiler plugin to annotate definitions with some specification that declares for which Scala version a definition should be included in the binaries. Since we don’t have to hold up the strict binary compatibility guarantees for new Scala versions, such a solution would also allow us to make some other binary incompatible changes. The downside would be similar to those of any preprocessor. We’d still need to keep some cruft along in the source files for a while but that burden seems similar to deprecations and less severe than having to maintain complete copies of some source files.

Anyone else having experience with this? Any other options we haven’t thought of?

Johannes

2 Likes

FWIW, we’re considering reverting the behavior of varargs in 2.13 to match that of 2.12: https://github.com/scala/scala-dev/issues/591#issuecomment-458211898, your input on that issue would be appreciated since it seems like this has a big impact on your codebases.

1 Like

Interesting, thanks for the pointer, @smarter.