Description
The following compiles with Scala 2 but not with Scala 3:
trait Monad[F[_]] {
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
final case class Dual[A](getDual: A) extends AnyVal
object Dual {
implicit val dualInstances: Monad[Dual] = new Monad[Dual] {
def flatMap[A,B](fa: Dual[A])(f: A => Dual[B]): Dual[B] = f(fa.getDual) // error in Scala 3, OK in Scala 2
}
}
The error we get is:
bridge generated for member method flatMap[A, B](fa: Dual[A])(f: A => Dual[B]): Dual[B] in anonymous class Object with Monad {...}
which overrides method flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] in trait Monad
clashes with definition of the member itself; both have erased type (fa: Object, f: Function1): Object.
This is a known fundamental problem with value classes: we need two flatMap
methods depending on whether we're in a context where we know we're dealing with Dual[A]
(and thus can erase it to its underlying type) or in a generic context (and thus need to keep Dual[A]
boxed), but the generic type parameter erases to Object
and the unboxed representation of this value class is also Object
, so we end up with two methods with the same signature. In fact, Scala 2 will emit the same error if we make the subclass non-anonymous:
object Dual {
class MonadDual extends Monad[Dual] {
def flatMap[A,B](fa: Dual[A])(f: A => Dual[B]): Dual[B] = f(fa.getDual)
}
implicit val dualInstances: Monad[Dual] = new MonadDual // error in Scala 2 and 3
}
So how come the first example works in Scala 2? Turns out there's a special-case just for anonymous classes, where the non-generic method gets renamed because it cannot be called anyway: scala/scala#3428. We could do the same to make cross-compilation easier, this can be done post-3.0 since it doesn't change anything for code that currently compiles.