[Pitch] Reference equivalent to value-type 'enum'


(James F) #1

I haven't tried any languages which have anything like this, but I support the idea of bringing real properties and single-case functions to enums in this way. Using properties rather than parameterised values will prevent pattern matching in switch statements to get those properties, but the idea of generic parameters being able to hold values gets thrown about a lot, which could eventually provide that functionality to enum classes, making pure enums a subset of their functionality.

If there's one fault I can see with this proposal, it's that it's lacking a value-type equivalent, an ‘enum struct’. :wink:

------------ Begin Message ------------
Group: gmane.comp.lang.swift.evolution
MsgID: <CANGnqV2O8eEo=Kw+fd9phpE_KT0NyvstrgNSHBBtTrj6gnpH7Q@mail.gmail.com>

Hello swift-evolution:

Based on recent conversations on the list, I'd like to float a trial
balloon: an "enum class" kind which is analogous to classes in the same way
existing enums are to structs. This is a data type which allows the user to
define a number of cases, like enums, and can participate in pattern
matching and exhaustivity analysis. Instances of an enum class are
reference types, which enables (for example) graph nodes with a built-in
concept of identity.

To be slightly more fanciful, perhaps such a kind could be treated as an
'almost-final' class, with each case being a nested type which is defined
as a final subclass of the enum class. Cases could then define their own
behavior:

enum class GraphNode {
  case Node(left: GraphNode, right: GraphNode) {
    override func foo() { ... }
    func nodeSpecificMethod() { ... }
  }

  case Leaf {
    override func foo() { ... }
    func leafSpecificMethod() { ... }
  }

  func foo() { ... }
}

let x : GraphNode = GraphNode.newEmptyTree()
let y : GraphNode = GraphNode.Node(l, r)
let z : GraphNode = GraphNode.Leaf()
let a : GraphNode.Leaf = GraphNode.Leaf()

Enum classes would not be subclassible, and extensions would not be able to
define new cases (as is the case with normal enums at the present time).

My superficial analysis seems to suggest that this would solve two issues:
providing a reference-semantics type with all the pattern matching
functionality of current enums, and providing a construct for modeling
case-class style ADTs where each case should be treated as a subtype (as
has been occasionally proposed).

I would like feedback as to:
- Whether this is something that would be useful enough to justify
additional language features
- Whether this is something that would be theoretically well-founded and
elegant

Thanks for your time, and have a great day!

Austin

------------- End Message -------------

From James F


(Austin Zheng) #2

Thanks!

I'm sadly not very coherent at this hour, but I just wanted to make a few small notes:

- I'm neutral as to whether stored properties should be allowed in the design. The value type memory layout issues which prohibit stored properties in normal enums won't apply to 'enum classes'. However, there may be other reasons to prohibit them.

- If enum classes' cases should be allowed to define per-case methods and stored properties (as opposed to just overriding those from the base class), two modes of interaction naturally fall out of the existing semantics. The user can either pattern match against the case itself (case .Node(let left, right)), which emphasizes the shared behavior, or they can use the downcast pattern match (case let myLeaf as Graph.Leaf), which emphasizes the per-case behavior.

- The "should enum cases be distinct but related types" question has been debated occasionally on the list. I think the proposed design has a duality where reference enums provide opt-in distinct type behavior naturally through subclassing, while value enums continue behaving the way they do currently. Value type inheritance has been mentioned, but if it goes in at all it will probably resemble implicit conversions far more than 'traditional' OO inheritance. This proposal tries to incrementally extend existing class/struct semantics to produce something that (hopefully) won't surprise developers who try using it.

Austin

···

On May 5, 2016, at 1:25 AM, James Froggatt via swift-evolution <swift-evolution@swift.org> wrote:

I haven't tried any languages which have anything like this, but I support the idea of bringing real properties and single-case functions to enums in this way. Using properties rather than parameterised values will prevent pattern matching in switch statements to get those properties, but the idea of generic parameters being able to hold values gets thrown about a lot, which could eventually provide that functionality to enum classes, making pure enums a subset of their functionality.

If there's one fault I can see with this proposal, it's that it's lacking a value-type equivalent, an ‘enum struct’. :wink:

------------ Begin Message ------------
Group: gmane.comp.lang.swift.evolution
MsgID: <CANGnqV2O8eEo=Kw+fd9phpE_KT0NyvstrgNSHBBtTrj6gnpH7Q@mail.gmail.com>

Hello swift-evolution:

Based on recent conversations on the list, I'd like to float a trial
balloon: an "enum class" kind which is analogous to classes in the same way
existing enums are to structs. This is a data type which allows the user to
define a number of cases, like enums, and can participate in pattern
matching and exhaustivity analysis. Instances of an enum class are
reference types, which enables (for example) graph nodes with a built-in
concept of identity.

To be slightly more fanciful, perhaps such a kind could be treated as an
'almost-final' class, with each case being a nested type which is defined
as a final subclass of the enum class. Cases could then define their own
behavior:

enum class GraphNode {
case Node(left: GraphNode, right: GraphNode) {
   override func foo() { ... }
   func nodeSpecificMethod() { ... }
}

case Leaf {
   override func foo() { ... }
   func leafSpecificMethod() { ... }
}

func foo() { ... }
}

let x : GraphNode = GraphNode.newEmptyTree()
let y : GraphNode = GraphNode.Node(l, r)
let z : GraphNode = GraphNode.Leaf()
let a : GraphNode.Leaf = GraphNode.Leaf()

Enum classes would not be subclassible, and extensions would not be able to
define new cases (as is the case with normal enums at the present time).

My superficial analysis seems to suggest that this would solve two issues:
providing a reference-semantics type with all the pattern matching
functionality of current enums, and providing a construct for modeling
case-class style ADTs where each case should be treated as a subtype (as
has been occasionally proposed).

I would like feedback as to:
- Whether this is something that would be useful enough to justify
additional language features
- Whether this is something that would be theoretically well-founded and
elegant

Thanks for your time, and have a great day!

Austin

------------- End Message -------------

From James F
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution