Sum-tuples

This idea came out of the “Adding Result to the Standard Library” thread. There was a diversion to an Either generic enumeration type with case .left with a payload of the first generic argument and .right with a payload of the second generic argument. There was someone wondering if the concept could be extended to more than two types, and concern over the individual names (especially due to the lack of extension to more-than-two names).

(Some of the posts have official replies. Others have their replies unmarked but directly following their referent post.)

I first came up with replacing Either with a general enum-equivalent to a tuple:

(| Type1, Type2 |)

but later posts said that the member names were important when using Either, even when the members were of the same type. So, with some rethought:


Current tuples will be called “product-tuples” when distinguishing is needed. I have three kinds of sum-tuples in mind:

  • regular: (| Type1, Type2, ... |)
  • type-collapsed: (< Type1, Type2, ... >)
  • unchecked: @unsafe(< Type1, Type2, ... >)

These are new kinds of (value) types, and as such have new metadata, witness tables, etc. in the ABI. This should be an additive change.

Unchecked tuples have no discriminant; you can switch between them at will while reading and writing. Such usage is effectively an unsafeBitCast if at least one type differs from another. If writing one type leads to a bit pattern that traps when reading as another, too bad.

Unchecked tuples are like a block of bytes with the minimal size/alignment/stride to work with all the member types.

The other two sum-tuple types also need to fit a discriminant. (Maybe a discriminant can be skipped if the number of member types is less than two.) A type-collapsed tuple has the same ABI-level implementation as a regular sum-tuple; just the user-level interfaces differ.

A regular sum-tuple has the same format as a product-tuple: a list of types, any of which may have a label. Regardless of an identifier label, each member has a number label (too). The brackets are different, with a “|” to suggest OR.

Technical Question: Can the brackets abut, i.e.(||)”? Or will the parsing rules require some kind of separator between them, like “(| |)”? What about “(<” and “>)”?

Opinion: Should Never be redone to: “typealias Never = (| |)”?

Opinion: For the type-collapsed and unchecked tuples, should occurrence-count and order matter when comparing two tuple types (of the same kind)? In other words, does type equivalence work like an Array or a Set? (Of course, it always matters for a regular sum-tuple.)

The regular value-type rules apply when passing around a sum-tuple instance as a whole. But when you want to work with a particular member:

  • Unchecked

    • When assigning to a tuple, just assign; it’ll work as long as the source is of one of the member types.
    • When reading or for in-out, use: x as MyType. This syntax works for assigning too if you want.
  • Regular

    • Accessing a member, for reading, in-out, or assignment-receiving, acts like an Optional of the member’s type. This includes using “!” during receiving an assignment to ensure that the instance was already using that member before the assignment. If the member itself was Optional, then the usual double-Optional rules apply.

    • The case tricks with switch/if/guard can be used to match which member is in use. Like nominal enumerations, a member label can be associated with or without a variable:

      var x: (| left: Int, right: Float, Double |) = //...
      
      switch x {
      case .left:  //...
      case .right(3.14):  //...
      case .right(let xx):  //...
      case .2:  //...
      }
      
  • Type-Collapsed

    • For reading, can use x as? MyType to Optionally check. Or use “!” to ensure or crash.
    • The “!”-form can be used for in-out or receiving assignments.
    • Like unchecked tuples, you can also directly receive an assignment.
    • For cases, use “case is MyType” for checking without caring about the value, or “let x as MyType” (or with var) if you need the value.

The research has burnt me out for the moment. Maybe I’ll reply with some support functions.

Oh, other threads include:

@mdiep Brought this up on Twitter awhile back: https://twitter.com/mdiep/status/971464033764347906

I added my thoughts recently: https://twitter.com/stephencelis/status/972909910366347266

This spawned this thread by @John_McCall just yesterday: https://twitter.com/pathofshrines/status/975779103126237184

1 Like