[swift-evolution] [Pitch] Property reflection

Matthew Johnson matthew at anandabits.com
Fri May 27 17:48:24 CDT 2016


> On May 27, 2016, at 5:37 PM, Anders Ha <hello at andersio.co> wrote:
> 
>> 
>> On 28 May 2016, at 6:00 AM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>>>> 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.
> 
> In this case, I wonder if it would be less confusing if bound lenses are available only on reference types, or existential with a class bound.

It could be very useful be able to pass a lens bound to a value type as a nonescaping argument.  The implementation would have to guarantee the bound lens can’t escape the effective scope of the value.  I don’t know how practical the implementation of that would be though.  It might require linear types along the lines of Rust in which case they would have to wait at least until Swift has such a system.

> 
>> 
>>>> 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
>> Architechies
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160527/d6d0b182/attachment.html>


More information about the swift-evolution mailing list