[swift-evolution] [Proposal] Property behaviors

Tino Heth 2th at gmx.de
Sat Jan 16 06:39:33 CST 2016


I'm always in favor of removing special cases (like lazy and willSet/didSet), so the idea of property behaviors itself is very appealing to me.
There's one downside, though:
The proposal makes some keywords obsolete, but introduces a whole bunch of new stuff in exchange — and all of this only can be used with properties…

I hope you agree with me that there is merit in keeping the language small (it's called "Swift", not "Turkey" ;-), and that the proposal would improve if it's possible to slim it down.

Imho the first step to add property behaviors should be refining how properties are modeled in Swift:
In many languages, properties are represented as a pair of methods (setter and getter), and their absence has several downsides (functional programming works best with functions ;-).

As soon as there is a way to read and write properties in a functional way, their behaviors could be expressed in a universal manner:
Afaics, all major use cases for behaviors can be implemented as simple "decorators" — in fact, I could simulate most examples from the proposal in a tiny playground (a part of it is attached).
Of course, the simulation is ugly, but I am confident that some nice ideas would come up if properties were more accessible.

Best regards,
Tino

//: Properties are modelled as a getter/setter pair
struct Property<T> {
	var get: Void -> T
	var set: (T) -> Void
}

class Test {
	// A field to store the actual data
	var mValue: Int?

	// This is our "pseudo-property": Just imaging that `self.value.get` maps to `self.value`
	var value = Property(get: { return 0 }, set: { Int in } )

	init() {
		// This is necessary because we need `self` to fake the getter/setter
		value = Property(get: { return self.mValue! }, set: { self.mValue = $0 })
	}
}


/*:
Behaviors are modelled as a transformation:
The take a set of functions and return another set the is extended according to the behavior.

A real implementation would gain more clarity from the (deprecated...) currying-syntax
*/
func didSetBehavior<T>(property: Property<T>, didSet: (T) -> Void) -> Property<T> {
	func setter(value: T) {
		property.set(value)
		didSet(value)
	}
	return Property(get: property.get, set: setter)
}

class DidSet: Test {
//: Imagine we have no superclass, and `value` is declared like

/*:
	var[didSetBehavior {
		print("New value is \($0)")
	}] value: Int
*/
	override init() {
		super.init()
		value = didSetBehavior(value) {
			print("New value is \($0)")
		}
	}
}

func willSetBehavior<T>(property: Property<T>)(allow: (old: T, new: T) -> Bool) -> Property<T> {
	func setter(new: T) {
		if allow(old: property.get(), new: new) {
			property.set(new)
		}
	}
	return Property(get: property.get, set: setter)
}

class WillSet: Test {
	override init() {
		super.init()
		self.value = willSetBehavior(value)(allow: { return $0 != $1 } )
	}
}

class WillSetDidSet: WillSet {
	override init() {
		super.init()
		self.value = didSetBehavior(value) {
			print("New value is \($0)")
		}
	}
}

func copyBehavior<T: NSCopying>(property: Property<T>) -> Property<T> {
	func performCopy(value: T) {
		let copy = value.copyWithZone(nil) as! T
		property.set(copy)
	}
	return Property(get: property.get, set: performCopy)
}

class Copy {
	var mValue = NSString()
	var value = Property(get: { return NSString() }, set: { NSString in return } )

	init() {
		value = Property(get: { return self.mValue }, set: { self.mValue = $0 })
		self.value = copyBehavior(value)
	}

}

let copyTest = Copy()
print(copyTest.value.get())
var mutableString = NSMutableString(string: "Mutable")
copyTest.value.set(mutableString)
mutableString.appendString(" string")
print(copyTest.value.get())

func decorate<In, Out, Discard>(function: (In) -> Out, decoration: (In) -> Discard) -> (In) -> Out {
	func result(input: In) -> Out {
		decoration(input)
		return function(input)
	}
	return result
}

class FuncTest {
	var f = { (input: String) in
		print(input)
	}

	init() {
		self.f = decorate(f) { input in
			print("Our input is \(input)")
		}
	}
}

FuncTest().f("duplicated")

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160116/3fc09dac/attachment-0001.html>


More information about the swift-evolution mailing list