SIP: Widening of case class apply to better emulate ADTs
See original GitHub issueMotivation
In Scala, case classes are used to emulate algebraic data types. But the emulation is not exact, insofar as a case class represents its own type whereas a constructor of an ADT has as result type the type of the underlying ADT type. For instance, using
trait Option[+T]
case class Some[T](x: T) extends Option[T]
case object None extends Option[Nothing]
the type of Some(1)
is Some[Int]
, not Option[Int]
. While useful, this can also cause problems for type inference. This SIP proposes an alternative class and object syntax, using :
for extends
. The only difference between the two forms concerns the auto-generated apply
method of a case class. If extension is via a :
, apply
has as return type the conjunction of all parent types following the :
.
Example: Using
trait Option[+T]
case class Some[T](x: T): Option[T]
case object None: Option[Nothing]
we have as most precise typings:
Some(1): Option[Int]
None: Option[Nothing]
Proposal in detail
In SyntaxSummary.txt
change
TemplateOpt ::= [`extends' Template | [nl] TemplateBody]
to
TemplateOpt ::= [(`extends' | `:') Template | [nl] TemplateBody]
Add the restriction that :
can be used only for case classes. (Not sure we need this, should we allow `:’ for normal classes as well? It would make no difference there).
Augment the definition in the spec on Case Classes as follows.
If the case class definition is defined with a :
extension clause, as in
C[tps](ps1)...(psn): parents {...}
the following apply
method is generated instead, where
P1, ..., Pn
are the declared or inferred parent types of C
:
object C {
def apply[tps](ps1)...(psn): P1 & ... & Pn = new C[tps](xs1)...(xsn)
}
Augment the definition in the spec on object definitions as follows: After the existing
It is roughly equivalent to the following definition of a lazy value:
lazy val m = new P1 with ... Pn { this: m.type => stats }
add the clause: if the case object definition is defined with a :
extension clause, the associated object is given the type P1 & ... & Pn
, as in:
lazy val m: P1 & ... & Pn = new P1 with ... Pn { this: m.type => stats }
Issue Analytics
- State:
- Created 8 years ago
- Reactions:1
- Comments:45 (26 by maintainers)
Top GitHub Comments
@odersky
Perhaps simpler to change the spec, but it would be a much greater cognitive stumbling block. There’s nothing intuitive about adopting C++/C#'s “extends” keyword (the
:
) but only in certain places, and the difference has to do with something random.I would be more open to the following, instead of only allowing it on case classes:
If a
:
is used in a class definition in pace ofextends
, then the compiler will generate anapply
method on the companion taking the same arguments as the primary constructor with a return type of the type following the:
. If the class is a case class, then thisapply
method is generated instead of the normal case class apply method (i.e. with the class type as its return type).Thus,
:
becomes a general rule, that intersects withcase class
simply by taking precedence in the one area of conflict.However, ONCE AGAIN, WE REALLY TRULY SERIOUSLY VERY BADLY need a lightweight syntax for defining ADTs and enums. So can we PLEASE PLEASE PLEASE get syntax for that, and then revisit the “problem” that is the premise of this issue in that context?
I think this vastly complicates the language and the cognitive burden on people learning Scala. What’s wrong with allowing one to override the
apply
method of the companion object, like:Another possibility would be to add an annotation, something like
But in general we already have
Option.empty
andOption.apply
. You can use those if needed.