A Guide on Binary Compatibility - need your input!

Oh yes.

Varargs versus Seq

def foo(xs: Int*): Int = xs.sum

↔

def foo(xs: Seq[Int]): Int = xs.sum

Source incompatible, but binary compatible. (both ways)

Change public to protected[Scope]

object Enclosing {
  def foo(x: Int): Int = x
}

→

object Enclosing {
  protected[Enclosing] def foo(x: Int): Int = x
}

Add a public method

final class Foo {
}

→

final class Foo {
  def bar(): String = "bar"
}

is source incompatible for a user that has

implicit class FooExt(private self: Foo) extends AnyVal {
  def bar(): String = "my bar"
}

def main(): Unit = {
  val foo: Foo = ???
  println(foo.bar())
}

Upgrading Foo without recompiling main preserves the old behavior of printing "my bar" (so binary compatible), but if main is recompiled, it now prints "bar" (source incompatible).

I could probably come up with other things, but those are the ones that immediately pop to my mind. The last one is particularly important to understand: it means that evolving a Scala library in a 100% source compatible way is virtually impossible. Maintaining binary compatibility is easier!

3 Likes