Tuple Initialization

I’ve been wanting  tuple-initializable in Swift for Core Graphics, where it’s a drag to always use long and cumbersome initializers when I’m not building production code:

let point: CGPoint = (100, 50)

Yeah, it is better form to use labeled initializers but I’m anyone using CGPoint understands the correspondence between (x, y) coordinates. And I don’t want to just build CGPoint.init(_ x:, _ y:) extensions. I like the tuple form.

Right now, the closest you can get is the silly:

let point = (CGPoint.init as (CGFloat, CGFloat) -> CGPoint)(100, 50)

And that’s (pardon the pun) pointless.

It would be pretty cool to allow memberwise tuples with or without labels in place of  initializers when the tuple field types could be unambiguously determined:

let point: CGPoint = (x: 100, y: 50) 
// instead of: let point = CGPoint(x: 100, y: 50)
let person: Person = ("Mary", nil, "Santos")
// instead of: let person = Person(first: "Mary", middle: nil, last: "Santos")

It may be ugly but it would be hella useful in Playgrounds.

4 Comments

  • One idea I had to address this a while back was this: Just as .something is interpreted as static member of the contextual type, .(x: 2, y: 30) could be interpreted as an initialiser of the contextual type.

    • That’s actually kinda already the case, it’s just .init instead of just . 😉

      For example you can already write this:


      let point: CGPoint = .init(x: 2, y: 30)

  • Best workaround I found so far is:

    import CoreGraphics
    
    protocol TupleInitializable1 {
      associatedtype Param1
      static var tupleinit: (Param1) -> Self { get }
    }
    func q(_ p1: T.Param1) -> T {
      return T.tupleinit(p1)
    }
    
    protocol TupleInitializable2 {
      associatedtype Param1
      associatedtype Param2
      static var tupleinit: (Param1, Param2) -> Self { get }
    }
    func q(_ p1: T.Param1, _ p2: T.Param2) -> T {
      return T.tupleinit(p1, p2)
    }
    
    protocol TupleInitializable3 {
      associatedtype Param1
      associatedtype Param2
      associatedtype Param3
      static var tupleinit: (Param1, Param2, Param3) -> Self { get }
    }
    func q(_ p1: T.Param1, _ p2: T.Param2, _ p3: T.Param3) -> T {
      return T.tupleinit(p1, p2, p3)
    }
    
    
    
    
    extension CGPoint: TupleInitializable2 {
      static let tupleinit: (Double, Double) -> CGPoint = CGPoint.init
    }
    
    struct Person: TupleInitializable3 {
      let firstName: String
      let middleName: String?
      let lastName: String
      static var tupleinit: (String, String?, String) -> Person = Person.init
    }
    
    let point: CGPoint = q(10.0, 30.0)
    let person: Person = q("Alice", nil, "Watson")
    

    Clearly not ideal, but ¯\_(ツ)_/¯

  • Instead of a tuple how about an array?

    extension CGPoint: ExpressibleByArrayLiteral {
        public init(arrayLiteral elements: CGFloat...) {
            assert(elements.count == 2)
            self.init(x: elements[0], y: elements[1])
        }
    }
    
    let point: CGPoint = [100, 50]