I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = #property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
I think you can already do that with `UnsafeMutablePointer`. I think. I'm really tired, so maybe if I look at it again in the morning I'll immediately see a difference. Apart from the syntax, of course, which is nice.
-Dave Sweeris
···
Sent from my iPhone
On Mar 14, 2017, at 01:02, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
// once this completes, myView.frame.origin.x == 120
animate(view: myView, property: property(UIView.frame.origin.x), amount: 100)
// once this completes, myView.frame.size.width == 198
animate(view: myView, property: property(UIView.frame.size.width), amount: 99)
I think this would be a pretty neat feature to have, what do you think?
I think you can already do that with `UnsafeMutablePointer`. I think. I'm really tired, so maybe if I look at it again in the morning I'll immediately see a difference. Apart from the syntax, of course, which is nice.
You definitely shouldn't try to replicate this with a pointer. At least one place that will break down is computed properties (as well as `didSet` and friends), but I imagine there are more. Also, this sort of operation should not require dropping down to an unsafe construct!
It's worth noting this is sort of equivalent to defining a type that stores functions that, given a type, will get or set a certain property. It would need to be initialized with 2 lambdas though.
···
On Mar 14, 2017, at 1:42 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:
On Mar 14, 2017, at 01:02, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:
On Mar 14, 2017, at 03:51, jaden.geller@gmail.com wrote:
On Mar 14, 2017, at 1:42 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:
Sent from my iPhone
On Mar 14, 2017, at 01:02, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
// once this completes, myView.frame.origin.x == 120
animate(view: myView, property: property(UIView.frame.origin.x), amount: 100)
// once this completes, myView.frame.size.width == 198
animate(view: myView, property: property(UIView.frame.size.width), amount: 99)
I think this would be a pretty neat feature to have, what do you think?
I think you can already do that with `UnsafeMutablePointer`. I think. I'm really tired, so maybe if I look at it again in the morning I'll immediately see a difference. Apart from the syntax, of course, which is nice.
You definitely shouldn't try to replicate this with a pointer. At least one place that will break down is computed properties (as well as `didSet` and friends), but I imagine there are more. Also, this sort of operation should not require dropping down to an unsafe construct!
It's worth noting this is sort of equivalent to defining a type that stores functions that, given a type, will get or set a certain property. It would need to be initialized with 2 lambdas though.
Lenses <https://github.com/typelift/Focus>\! My only concern is that arbitrary effects can be attached to setters and getters, which can lead to surprising results when using the property reference. As a language feature, I’d be interested to see where discussion around this will lead.
~Robert Widmann
···
On Mar 14, 2017, at 4:02 AM, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
On Wed, Mar 15, 2017 at 1:59 AM, Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:
Lenses <https://github.com/typelift/Focus>\! My only concern is that
arbitrary effects can be attached to setters and getters, which can lead to
surprising results when using the property reference. As a language
feature, I’d be interested to see where discussion around this will lead.
~Robert Widmann
On Mar 14, 2017, at 4:02 AM, Andrew Thompson via swift-evolution < > swift-evolution@swift.org> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow
properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.
x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in
our code. For example, we can animate any property on a view (that is
documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>,
amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
On Mar 14, 2017, at 1:02 AM, Andrew Thompson via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
That sounds pretty good. I find the response from the community to this proposal to be pretty cool.
While reading some of the discussion around your proposal, I’ve seen requests where people are wanting to retrieve the list of an object’s properties. I think this is an interesting idea to explore, particularly if it was possible to generalise about all of the properties stored on an object.
For example, consider the following:
We have a protocol, JSONEncodable, and a bunch of types that conform to it, namely `Int`, `String`, and `Data`.
But this seems a little bit repetitive, what we really want is for the library author to declare the following:
For every type T whose properties conform to protocol P, we can derive a free conformance to protocol P for type T.
Applying this, we get the following code:
extension JSONEncodable where Self.InstanceProperties: JSONEncodable {
func encode() -> String {
let properties = Metatype<Self>.properties
var output = “”
for p in properties {
output += p.read(self).encode()
}
return output
}
}
Now all a user needs to do is say that want to conform to the protocol:
struct Person: JSONEncodable {
var age: Int
var name: String
var identifier: Data
}
Cheers,
- Andrew
···
On 18 Mar 2017, at 6:08 am, Joe Groff <jgroff@apple.com> wrote:
On Mar 14, 2017, at 1:02 AM, Andrew Thompson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}
That sounds pretty good. I find the response from the community to this proposal to be pretty cool.
While reading some of the discussion around your proposal, I’ve seen requests where people are wanting to retrieve the list of an object’s properties. I think this is an interesting idea to explore, particularly if it was possible to generalise about all of the properties stored on an object.
I agree, I think that's a natural extension of the core key path functionality. We're trying to keep the initial work small in scope to begin with, but I think it's a good foundation to start building out a better reflection model.
-Joe
···
On Mar 19, 2017, at 6:25 PM, Andrew Thompson <mrwerdo331@me.com> wrote:
For example, consider the following:
We have a protocol, JSONEncodable, and a bunch of types that conform to it, namely `Int`, `String`, and `Data`.
But this seems a little bit repetitive, what we really want is for the library author to declare the following:
For every type T whose properties conform to protocol P, we can derive a free conformance to protocol P for type T.
Applying this, we get the following code:
extension JSONEncodable where Self.InstanceProperties: JSONEncodable {
func encode() -> String {
let properties = Metatype<Self>.properties
var output = “”
for p in properties {
output += p.read(self).encode()
}
return output
}
}
Now all a user needs to do is say that want to conform to the protocol:
struct Person: JSONEncodable {
var age: Int
var name: String
var identifier: Data
}
Cheers,
- Andrew
On 18 Mar 2017, at 6:08 am, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
On Mar 14, 2017, at 1:02 AM, Andrew Thompson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello Swift Evolution Community,
I’ve been thinking about a new language feature that would allow properties to be first class citizens. The basic idea is as follows:
let x: PropertySelector<UIView, CGFloat> = property(UIView.frame.origin.x)
let view: UIView = …
view.frame.origin.x = 20
x.read(view) // returns 20
x.write(view, value: 9091)
view.frame.origin.x // now 9091
This is a trivial example, but now we can do more interesting things in our code. For example, we can animate any property on a view (that is documented to be animatable of course):
func animate(view: UIView, property: PropertySelector<UIView, CGFloat>, amount: Int) {
let originalValue = property.read(view)
func generateKeyFrames() {
let step = 1.0 / Double(amount)
for i in 0..<amount {
let newValue = originalValue + CGFloat(i)
let time = Double(i) / Double(amount)
UIView.addKeyframe(withRelativeStartTime: time,
relativeDuration: step,
animations: { property.write(view, value: newValue) }
)
}
}