[swift-evolution] Draft proposal: multi-property assignment .= operator

Michel Fortin michel.fortin at michelf.ca
Sun Jan 10 14:21:26 CST 2016


(This proposal came from thinking about the memberwise initializer proposal as well as older proposals for "cascading" and creating scopes making some members act like local variables.)

I'd like to propose a syntax to set multiple properties at once. It would look like this:

	var object = MyObject()
	object .= (
		property1: 1,
		property2: "a"
	)

and be equivalent to this:

	var object = MyObject()
	object.property1 = 1
	object.property2 = "a"
	
What the `.=` operator does is take each value in the tuple on the right and assign them to the property of the same name on the variable on the left. Assignments are performed in the same order as they're defined in the tuple.

The tuple on the left of the `.=` operator can be written on the spot (as above) or be the result of an arbitrary expression, like here:

	var object = MyObject()

	var values = (property1: 1, property2: "b")
	object .= values

	func makeValuesFor(value: Int) -> (property1: Int, property2: String) {
		return (value, "\(value)")
	}
	object .= makeValuesFor(4)

If the tuple contains property names that do not exist in the assigned variable, or if there is a mismatch in type or visibility and the assignment cannot happen, this is a compile-time error.

This syntax is particularly beneficial when assigning to properties of a deeply nested value:

	object.subpart.detail.numberPad .= (
		radix: 9
		position: .Top
		font: .System
	)


## Tentative Implementation

It's almost possible already to implement this with reflection. Here's an attempt (using a different operator name because `.=` doesn't work as a custom operator):

	infix operator ~= {  }

	func ~= <T>(inout target: T, values: (a: Int, b: String)) {
		let valuesMirror = Mirror(reflecting: values)
		let targetMirror = Mirror(reflecting: target)
		valueLoop: for valueField in valuesMirror.children {
			guard let label = valueField.label else {
				fatalError("Missing label in value tuple.")
			}
			for targetField in targetMirror.children {
				print(targetField)
				if targetField.label == label {
					targetField.value = valueField.value
					continue valueLoop
				}
			}
		}
	}

This fails because you can't assign to fields using the Mirror API. If this line could be replaced by something that works:

	targetField.value = valueField.value

then we could have a library implementation.


-- 
Michel Fortin
https://michelf.ca



More information about the swift-evolution mailing list