Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
LPTK opened this issue Mar 6, 2017 · 4 comments
Closed

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

LPTK opened this issue Mar 6, 2017 · 4 comments

Comments

@LPTK
Copy link
Contributor

LPTK commented Mar 6, 2017

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?

@odersky
Copy link
Contributor

odersky commented Apr 4, 2017

Thanks for the proposal. I had a careful look but in the end decided to keep the original one for now. Let's see how it plays out in actual usage. If we find that some of the painpoints mentioned here are important, we can always go back and tweak it. So I'd like to leave the issue open for now.

I realize the whole thing is a judgement call. For now I believe that writing type parameters on cases is in fact also annoying so being able to avoid them is a bonus. I was also not sure how nominality rules would play out in practice.

@LPTK
Copy link
Contributor Author

LPTK commented Apr 4, 2017

Thanks for the feedback!

The "nominal extension" approach proposed here seems quite new compared to other Scala features, but after all it's not so different from, e.g. overriding (also nominal). Though I agree it's not so clear whether nominal extension is a good idea in the grand scheme of things.

@odersky
Copy link
Contributor

odersky commented May 17, 2017

After discussing this more with @sjrd I am coming around to the nominal type parameter idea. I am about to update the proposal accordingly, as a trial balloon.

@odersky
Copy link
Contributor

odersky commented Jan 24, 2018

This has been integrated by an large now. If further changes are proposed it would be god to make them in a separate issue.

@odersky odersky closed this as completed Jan 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants