[Idea] Creating an enums as a sum of multiple other enums

I’ve been writing an interpreter in Swift and have been finding enums incredibly useful. One feature thought that I thought would make life easier is the ability to create a super-enum that contains as its cases all the cases of its constituent enums:

enum UnaryOperator {
    case not
}

enum BinaryOperator {
    case and
    case or
}

case BooleanLiteral {
    case `true`
    case `false`
}

typealias Token = UnaryOperator | BinaryOperator | BooleanLiteral

It would then be possible to do something like this:

scanToken() -> Token

indirect enum Expr {
    case binary(op: BinaryOperator, lhs: Expr, rhs: Expr)
}

For example, a number of functions in the recursive descent parser can only return a subset of all possible expressions.

Of course it’s already possible to represent the same data structure like this:

enum Token {
    case binaryOperator(BinaryOperator)
    case unaryOperator(UnaryOperator)
    case booleanLiteral(BooleanLiteral)
}

Perhaps what I’m suggesting could just be syntactic sugar, but it’d make it much easier to switch over `Token` without worrying about all of the nested enums. The feature becomes even more useful if you think about a deeper hierarchy of enums. It would bring some of the power of protocols/POP to enums.

This raises a few questions such as:
- Case name collisions
- Associated types
- Raw values

But I can’t think of anything that cannot be addressed with sensible rules and conditions.

Interested to read your thoughts.

-Ahmad

Not a response to the core of this, but to the motivating example: Sectioned (or multi-sorted in other cases) ASTs can also be represented by a singly-sorted AST and views over top

enum UnaryOperatorView {
  case not
}

enum BinaryOperatorView {
  case and
  case or
}

enum BooleanLiteralView {
  case `true`
  case `false`
}

enum Token {
  case not
  case and, or
  case `true`, `false`
  
  var asUnaryOperator: UnaryOperatorView? {
    switch self {
    case .not: return .not
    default: return nil
    }
  }
  
  /*etc.*/
}

The Valence library uses this to great effect <https://github.com/typelift/Valence/blob/master/Tests/SystemF.swift#L111&gt;\.

~Robert Widmann

···

On Jul 31, 2017, at 5:43 PM, Ahmad Alhashemi via swift-evolution <swift-evolution@swift.org> wrote:

I’ve been writing an interpreter in Swift and have been finding enums incredibly useful. One feature thought that I thought would make life easier is the ability to create a super-enum that contains as its cases all the cases of its constituent enums:

enum UnaryOperator {
   case not
}

enum BinaryOperator {
   case and
   case or
}

case BooleanLiteral {
   case `true`
   case `false`
}

typealias Token = UnaryOperator | BinaryOperator | BooleanLiteral

It would then be possible to do something like this:

scanToken() -> Token

indirect enum Expr {
   case binary(op: BinaryOperator, lhs: Expr, rhs: Expr)
}

For example, a number of functions in the recursive descent parser can only return a subset of all possible expressions.

Of course it’s already possible to represent the same data structure like this:

enum Token {
   case binaryOperator(BinaryOperator)
   case unaryOperator(UnaryOperator)
   case booleanLiteral(BooleanLiteral)
}

Perhaps what I’m suggesting could just be syntactic sugar, but it’d make it much easier to switch over `Token` without worrying about all of the nested enums. The feature becomes even more useful if you think about a deeper hierarchy of enums. It would bring some of the power of protocols/POP to enums.

This raises a few questions such as:
- Case name collisions
- Associated types
- Raw values

But I can’t think of anything that cannot be addressed with sensible rules and conditions.

Interested to read your thoughts.

-Ahmad
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think what you're asking for here is essentially some sort of (or special
case of) value subtyping, a topic near and dear to many.

···

On Mon, Jul 31, 2017 at 7:43 PM, Ahmad Alhashemi via swift-evolution < swift-evolution@swift.org> wrote:

I’ve been writing an interpreter in Swift and have been finding enums
incredibly useful. One feature thought that I thought would make life
easier is the ability to create a super-enum that contains as its cases all
the cases of its constituent enums:

> enum UnaryOperator {
> case not
> }
>
> enum BinaryOperator {
> case and
> case or
> }
>
> case BooleanLiteral {
> case `true`
> case `false`
> }
>
> typealias Token = UnaryOperator | BinaryOperator | BooleanLiteral

It would then be possible to do something like this:

> scanToken() -> Token
>
> indirect enum Expr {
> case binary(op: BinaryOperator, lhs: Expr, rhs: Expr)
> }

For example, a number of functions in the recursive descent parser can
only return a subset of all possible expressions.

Of course it’s already possible to represent the same data structure like
this:

> enum Token {
> case binaryOperator(BinaryOperator)
> case unaryOperator(UnaryOperator)
> case booleanLiteral(BooleanLiteral)
> }

Perhaps what I’m suggesting could just be syntactic sugar, but it’d make
it much easier to switch over `Token` without worrying about all of the
nested enums. The feature becomes even more useful if you think about a
deeper hierarchy of enums. It would bring some of the power of
protocols/POP to enums.

This raises a few questions such as:
- Case name collisions
- Associated types
- Raw values

But I can’t think of anything that cannot be addressed with sensible rules
and conditions.

Interested to read your thoughts.

-Ahmad
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

That solution seems way too verbose, and also not exactly type safe because you must handle optionals in addition to any new cases that arise, which are silently handled by the default case.

To Xiaodi: is this really just value subtyping? Seems like it would also require multiple inheritance for it to work as suggested. To me this seems more like sugar for an Either type containing an arbitrary number of types (actually a special case of that because it’s restricted to enums).

Either way it gets a big +1 from me.

···

On Jul 31, 2017, at 10:04 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

Not a response to the core of this, but to the motivating example: Sectioned (or multi-sorted in other cases) ASTs can also be represented by a singly-sorted AST and views over top

enum UnaryOperatorView {
  case not
}

enum BinaryOperatorView {
  case and
  case or
}

enum BooleanLiteralView {
  case `true`
  case `false`
}

enum Token {
  case not
  case and, or
  case `true`, `false`
  
  var asUnaryOperator: UnaryOperatorView? {
    switch self {
    case .not: return .not
    default: return nil
    }
  }
  
  /*etc.*/
}

The Valence library uses this to great effect.

~Robert Widmann

On Jul 31, 2017, at 5:43 PM, Ahmad Alhashemi via swift-evolution <swift-evolution@swift.org> wrote:

I’ve been writing an interpreter in Swift and have been finding enums incredibly useful. One feature thought that I thought would make life easier is the ability to create a super-enum that contains as its cases all the cases of its constituent enums:

enum UnaryOperator {
   case not
}

enum BinaryOperator {
   case and
   case or
}

case BooleanLiteral {
   case `true`
   case `false`
}

typealias Token = UnaryOperator | BinaryOperator | BooleanLiteral

It would then be possible to do something like this:

scanToken() -> Token

indirect enum Expr {
   case binary(op: BinaryOperator, lhs: Expr, rhs: Expr)
}

For example, a number of functions in the recursive descent parser can only return a subset of all possible expressions.

Of course it’s already possible to represent the same data structure like this:

enum Token {
   case binaryOperator(BinaryOperator)
   case unaryOperator(UnaryOperator)
   case booleanLiteral(BooleanLiteral)
}

Perhaps what I’m suggesting could just be syntactic sugar, but it’d make it much easier to switch over `Token` without worrying about all of the nested enums. The feature becomes even more useful if you think about a deeper hierarchy of enums. It would bring some of the power of protocols/POP to enums.

This raises a few questions such as:
- Case name collisions
- Associated types
- Raw values

But I can’t think of anything that cannot be addressed with sensible rules and conditions.

Interested to read your thoughts.

-Ahmad
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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