[swift-evolution] [Accepted] SE-0155: Normalize Enum Case Representation

John McCall rjmccall at apple.com
Thu Apr 20 15:20:51 CDT 2017


Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md <https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md>

Hello Swift Community,

The review of SE-0155 "Normalize Enum Case Representation” ran from March 31st through April 10th, 2017. The proposal is accepted with revisions.

Feedback from the community was positive about most aspects of the proposal.  However, there was substantial disagreement about the right direction for pattern matching.  The core team discussed this issue in depth.

Pattern matching is central to the use of enum types.  It's the only way you can use an enum value, besides general operations like passing it to a function or the special affordances for Optionals.  Pattern matching is as central to enums as stored property access is to structs, and it's fair to be worried about anything that would make it substantially more onerous.  Unconditionally requiring associated-value labels in case patterns would certainly do that, and several members of the core team expressed concern that it would be bad enough to discourage the use of associated-value labels completely — in effect, subverting the entire language feature being proposed.

It is true that including associated-value labels in case patterns does preserve a great deal of information in the source code:

  - This information can usefully contribute to the clarity of the code following the pattern.

  - Hiding this information can lead to bugs that would be self-evident if the case labels were always included.  For example, if a case payload included a number of different boolean flags, it would be easy for a pattern to accidentally label them in the wrong order.

  - Finally, this information may be necessary in order to determine which case is being matched, since the proposal adds the ability to distinguish cases purely by the labels on associated values.

However, the core team feels that there are counter-arguments which weaken the force of these considerations:

  - While an associated-value label can indeed contribute to the readability of the pattern, the programmer can also choose a meaningful name to bind to the associated value.  This binding name can convey at least as much information as a label would.

  - The risk of mis-labelling an associated value grows as the number of associated values grows.  However, very few cases carry a large number of associated values.  As the amount of information which the case should carry grows, it becomes more and more interesting to encapsulate that information in its own struct — among other reasons, to avoid the need to revise every matching case-pattern in the program.  Furthermore, when a case does carry a significant number of associated values, there is often a positional conventional between them that lowers the risk of re-ordering: for example, the conventional left-then-right ordering of a binary search tree.  Therefore this risk is somewhat over-stated, and of course the programmer should remain free to include labels for cases where they feel the risk is significant.

  - It is likely that cases will continue to be predominantly distinguished by their base name alone.  Methods are often distinguished by argument labels because the base name identifies an entire class of operation with many possible variants.  In contrast, each case of an enum is a kind of data, and its name is conventionally more like the name of a property than the name of a method, and thus likely to be unique among all the cases.  Even when cases are distinguished using only associated value labels, it simply means that the corresponding case-patterns must include those labels; we should not feel required to force that burden on all other case-patterns purely to achieve consistency with this presumably-unusual style.

Accordingly, while it needs to be possible to include associated value labels in a case-pattern, and in some situations it may be wise to include them, the core team believes that requiring associated value labels would be unduly onerous.  Therefore, the core teams revises the proposal as follows:

A case pattern may omit labels for the associated values of a case if there is only one case with the same base name and arity.  A pattern must omit all labels if it omits any of them; thus, a case pattern either exactly matches the full name of a case or has no labels at all.  For example:

  enum E {
    case often(first: Int, second: Int)
    case lots(first: Int, second: Int)
    case many(value: Int)
    case many(first: Int, second: Int)
    case many(alpha: Int, beta: Int)
    case sometimes(value: Int)
    case sometimes(Int)
  }

  switch e {
  // Valid: the sequence of labels exactly matches a case name.
  case .often(first: let a, second: let b):
    ...

  // Valid: there is only one case with this base name.
  case .lots(let a, let b):
    ...

  // Valid: there is only one case with this base name and payload count.
  case .many(let a):
    ...

  // Invalid: there are multiple cases with this base name and payload count.
  case .many(let a, let b):
    ...

  // Valid: the sequence of labels exactly matches a case name.
  case .many(first: let a, second: let b):
    ...

  // Invalid: includes a label, but not on all of the labelled arguments.
  case .same(alpha: let a, let b):
    ...

  // Valid: the sequence of labels exactly matches a case name (that happens to not provide any labels).
  case .sometimes(let x):
    ...

  // Invalid: includes a label, but there is no matching case.
  case .sometimes(badlabel: let x):
    ...
  }

This only affects case patterns.  Constructing a case always requires that any associated value labels in the case name be provided.

A case pattern must include patterns for all associated values of the case, even if the associated value has a default value.  We may choose to relax this rule in a future release, or generally provide some sort of "..." syntax for indicating that there are associated values being ignored.

The proposal includes a rule inferring labels in case patterns from binding names.  The core team feels that imparting local variable names with this kind of significance would be unprecedented, surprising, and rather "pushy".  The goal of this rule is also largely achieved by the new rule allowing labels to be omitted regardless of binding.  Accordingly, this rule is struck from the proposal.  That said, it would be a good idea for the implementation to warn when a binding name matches the label for a different associated value.

John McCall
Review Manager
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170420/aa0772c0/attachment.html>


More information about the swift-evolution mailing list