[swift-evolution] [Pitch] Property reflection

Brent Royal-Gordon brent at architechies.com
Fri May 27 17:00:53 CDT 2016

>> I'm not sure how much metadata is necessary beyond name (in the key) and type (discussed soon).
> In the future we may have user-defined attributes.  It could be very useful to expose that.

This is true.

Actually, I'm wondering if property behaviors should be able to essentially declare alternate PropertyViews and add their properties to them. For instance:

	var behavior persistent<Value: PersistentValue>: Value where Self: ManagedObject {
		belongsto persistentProperties, static persistentInstanceProperties
		name: String
		var value: Value
		init() {
			value = entity.propertiesByName[name].defaultValue
		get { return value }
		set { value = newValue }
	protocol Managed {
		static var persistentProperties: InstancePropertyView<Self> { get }
		var persistentProperties: PropertyView<Self> { get set }
	class Person: Managed {
		@persistent var name: String
		@persistent var birthDate: Date
	print(Person.persistentProperties.keys)		// => "name", "birthDate"

I think it's probably more likely that you want to access the subset of properties with a particular attribute than that you want to look through all the properties and check whether each one has or doesn't have that attribute.

(Incidentally, this is beginning to look a little like one of the post- at memberwise proposals to have some sort of entity representing a group of properties—only more flexible and with a broader set of use cases. Still, there might be hope for a solution with a `@memberwise` behavior and a macro to look at the resulting property view and generate an initializer from it.)

>> Yes. I would hope that you could downcast a concrete lens:
>> 	let lens: () -> inout Int = &array.count					// Assuming this is a "get lens” syntax
> I think the syntax was actually a little bit different as the lens isn’t bound to the instance and a readonly lens wouldn’t return inout:
> let lens: (Array<MyType>) -> Int = &array.count

I envision there being both bound lenses (sorry, I forgot that `Array.count` isn't mutating):

	struct Person { var name: String }
	var joe = Person(name: "Joe")
	let lens: () -> inout String =  &joe.name

And unbound ones:

	let unboundLens: (inout Person) -> inout String = Person.name

`properties` on an instance would give you bound lenses; `properties` on a type would give you unbound ones. (Or maybe the latter would be something like `instanceProperties`, since type instances have properties too.)

On the other hand, the idea of bound lenses might not be coherent; the `inout` parameter of an unbound lens helps make sure that mutation affects value-typed instances, so bound lenses might not work. Perhaps `properties` on an instance should be more akin to a `[String: Any]` dictionary, so that `joe.properties["name"] = "Bob"` naturally calls `joe`'s `properties` setter. The problem I see there is that the `openas` might open things too far: If the property is declared to be of protocol type, `openas` would end up giving you the concrete type of its current value instead.

>> And that you could later open the abstracted lens to get its concrete type:
>> 	if let concreteLens = abstractLens openas Lens {
>> 		print(Lens.ReturnValue)
>>>> 	}
> What about casting like this:
> let typedLens = abstractLens as? (Array<MyType>) -> Int

If you're specifically expecting an `Int`, sure. But if you have no idea what the type is and want to find out, I think you'd need to (somehow) open the Any lens and see the concrete return type inside.

Brent Royal-Gordon

More information about the swift-evolution mailing list