Tuple `as struct`

Based on recently active topics such as Tuples conform to Equatable and Anonymous Structs, I'd like to throw my own pre-pitch onto the table as well: as struct syntax.

It's common for libraries to support functions that accept arbitrary dictionaries, one example being serializers:

func serialize(_ dictionary: [String: Any]) { ... } serialize( [ "name": "John Doe", "age": 40, "favoriteMeals": { "breakfast": "scrambled eggs", "lunch": "reuben", "dinner": "carbonara" } ] ) 

This approach has several problems:

  • There's no clean way to enforce at compile-time that that the given dictionary is actually serializable. JSONSerialization performs this check at run-time, and Encodable requires employing type-erasure.
  • Similarly, this approach makes you prone to duplicate keys, something one would either have to explicitly check at run-time, or allow to pass through undetected.

With these issues in mind, I propose new syntactical sugar that enables a tuple declaration to be instantiated as an anonymous struct. For example:

let myValue = (foo: "bar", one: 1, true) as struct print(myValue.foo) // "bar" print(myValue.2) // "true" 

would desugar into

struct __some_struct { var foo: String var one: String var _2: Bool } let myValue = __some_struct(foo: "bar", one: 1, _2: true) print(myValue.foo) // "bar" print(myValue._2) // "true" 

This would also support limited protocol conformance, transforming the earlier example into something like:

func serialize<T: Encodable>(_ value: T) { ... } serialize( ( name: "John Doe", age: 40, favoriteMeals: ( breakfast: "scrambled eggs", lunch: "reuben", dinner: "carbonara" ) as Encodable struct ) as Encodable struct ) 

Clearly, this pitch is still quite rough, but I'd love to get feedback from folks here, especially on any language design shortcomings I'm likely missing.

2 Likes

This has already been pitched before: Anonymous Structs

As I mentioned in the opening line, this Pre-Pitch is motivated by that discussion. However, I opted to break this out into its own thread as that one is already quite long (>130 posts) and has been dormant for several weeks now.

Additionally, the syntactical approach and capabilities between the pitches are quite different, with this as struct approach purposely targeting more minimal additions that compose well with current syntax. The approach proposed in Anonymous Structs requires the following grammar changes:

type-closure-expression → { type-capture-list } type-closure-expression → { statements? } type-closure-expression → { type-closure-signature in statements? } type-closure-expression → { type-closure-signature in getter-setter-block } type-closure-expression → { type-closure-signature in struct declarations? } type-closure-signature → type-capture-list? closure-parameter-clause throws? function-result? type-closure-signature → type-capture-list closure-parameter-clause → ( ) | ( closure-parameter-list ) | identifier-list closure-parameter-list → closure-parameter | closure-parameter , closure-parameter-list closure-parameter → closure-parameter-name type-annotation closure-parameter-name → identifier type-capture-list → [ type-capture-list-items ] type-capture-list-items → type-capture-list-item | type-capture-list-item , type-capture-list-items type-capture-list-item → attributes? type-capture-specifier? expression type-capture-specifier → var | weak var? | unowned var? | unowned(safe) var? | unowned(unsafe) var? 

by contrast, the pitch here only requires the following:

primary-expression → tuple-as-struct-expression tuple-as-struct-expression → tuple-expression as type? struct 

where type? in tuple-as-struct-expression is constrained to a protocol or protocol composition type.

Other differences include:

  • Direct member access support without explicit protocol conformance
  • No support for property wrapper syntax
  • All synthesized properties are var by default

Not sure if it was there originally, but maybe I missed it (sorry). It could still be worth suggesting this as an alternative in that thread though.