Scala 2
ADTs are commonly expressed in Scala 2 as:
sealed trait Foo
object Foo {
case class Bar(i: Int) extends Foo
case class Baz(b: Boolean) extends Foo
}
The problem with this approach is that we sometimes get wonky type inference:
scala> List(Foo.Bar(1), Foo.Baz(false))
res0: List[Product with Serializable with Foo] = List(Bar(1), Baz(false))
As a result, a common pattern is to write ADTs as:
sealed trait Foo extends Product with Serializable
object Foo {
case class Bar(i: Int) extends Foo
case class Baz(b: Boolean) extends Foo
}
The type inference issue then goes away:
scala> List(Foo.Bar(1), Foo.Baz(false))
res1: List[Foo] = List(Bar(1), Baz(false))
Dotty
In Dotty, we have the delightful enum
syntax for ADTs, which make a lot of the verbosity go away:
enum Foo {
case Bar(i: Int)
case Baz(b: Boolean)
}
And, due to smart data constructors, it’s almost as if the type inference issue had gone away:
scala> List(Foo.Bar(1), Foo.Baz(false))
val res2: List[Foo] = List(Bar(1), Baz(false))
Almost, though:
scala> List(new Foo.Bar(1), new Foo.Baz(false))
val res3: List[Foo & Product & Serializable] = List(Bar(1), Baz(false))
While a clear improvement, the problem is still here - better hidden, but still here. One might argue that no one should call constructors directly, but the point is that anybody could.
Possible fix
If my understanding of enum
is correct, Dotty would need to flag all enums that have at least 2 class cases as extending Product with Serializable
.
Simple and value cases are encoded as val
and are of the enum’s root type - they do not extend Product
or Serializable
and are not part of the problem.
If the enum has a single class case, there is no scenario I can think of in which Product
or Serializable
will be inferred.
Maybe the rule _any enum that contains 2 class cases or more will extend Product with Serializable
is too strange, though. It could be generalised - all enums, or all enums that have class cases, or…
There is potential for breakage with such a rule: what if the enum
defines simple cases that extend types that are not serializable, for example?