Skip to content

[enum construct] Name-based rules for implicit extension #2055

Closed
@LPTK

Description

@LPTK

This proposal is based on a perception that what makes the (G)ADT syntax so cumbersome in Scala is having to write out explicit extends clauses, not so much having to write out type parameters. PR #1958 by @odersky fixes that to some extent, but only in a limited way. I propose to generalize the way enum cases implicitly extend their parent enum.

A tentative implementation is available at https://github.com/LPTK/dotty/tree/add-enum.

The proposal is based on the original PR #1958, but makes the following changes:

  • The mechanism that insertes extends clauses for cases is generalized; it is now nominal: value and type parameters of a case that have the same name as parameters of the enum are passed to the enum constructor in the inserted extends clause.
    For example:
enum Opt[+T] {
  case Som[T](x: T)
  case Non
}
// expands to
sealed class Opt[+T]
object Opt {
  final case class Som[+T](x: T) extends Opt[T]
  val Non: Opt[Nothing] = new Opt[Nothing] { /* defs enumTag, toString */ }
}

This makes many things possible that are not possible with the current proposal, including automatic forwarding of value parameters and more flexible generic enums, for example having different type parameters in cases than those defined in the parent enum (see this GADT example).

  • Enum parameters not found as parameters in a case can be provided as members of the case.
    For example, this is a valid enum definition:
enum class E[T](val x: T) { val test: Int }
object E {
  case A[T](x: T) { val test = 43 }
  case B[T](x: T, test: Int)
  case C[S](y:S, test: Int) { type T = Int; val x = 123 }
  case D[T,S](x:T,y:S) extends Tester
}
trait Tester { val test = 43 }
  • Enum cases should never explicitly extends the enum class, although they can still extend arbitrary traits. I think the above two mechanisms are sufficient to remove the need for explicit extension.

  • In contrast to Add "enum" construct #1958, no type parameters are automatically added to enum cases. This has the benefit of avoiding confusion coming from various scoping paradoxes. However, the variance and bounds of type parameters are inherited from those of the corresponding enum class parameter (if any), so they need not be repeated.
    For example:

enum E[+T <: AnyRef] { case A[T,S]() }
// expands to
sealed class E[+T <: AnyRef]
object E { final case class A[+T <: AnyRef, S]() extends E[T] }
  • Value parameters of case classes now automatially override values in the parent. I am not sure whether this is really desirable all things considered; I implemented this both because it removes common boilerplate, and to get around Cannot override a val from the parent class #2051.

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions