Re: [swift-evolution] [Pitch] Improving KeyPath

2017-11-29 Thread Jon Gilbert via swift-evolution
I concur with Logan’s idea here on the general points, but let me add a bit 
more. 

Here are some KeyPathy things I’d like to see in a future Swift:

/// A set of PartialKeyPath guaranteed as:
/// (a) the entire set of keypaths for a type; and
/// (b) accessible given the current scope
Type.allKeyPaths() throws -> [PartialKeyPath]

/// A set of PartialKeyPath guaranteed as:
/// (a) sufficient for initialization of an instance; and 
/// (b) accessible given the current scope
Type.sufficientPartialKeyPaths() throws -> [PartialKeyPath]

class Property> {   
let keyPath: KeyPath
let value: ValueType
}

Type.init(with properties: [PartialProperty]) 

Type.init(copy: Type, overwriting properties: [PartialProperty])

The idea is a type can provide you a set of PartialKeyPath that is 
guaranteed as sufficient for initialization of an instance of the type, as long 
as the current scope lets you access it. 

What would also be nice:

/// A set of PartialKeyPath guaranteed as
/// (a) the entire set of writable keypaths of Type; and
/// (b) accessible given the current scope
AllWritableKeyPaths

(etc.) :D

Note: in Swift 3.2/4, (of course), AnyKeyPaths and PartialKeyPaths can 
already be downcast to more specific types like KeyPath, 
WritableKeyPath, etc., but only if you already know what T and E are at 
compile time (i.e. they are not generic). 

I have found some bugs though; iterating through arrays of AnyKeyPath using 
“where” statements to limit the types is a buggy and unpredictable affair (I 
believe “filter(into:)” works best). 

E.g.:

extension Array where Element == AnyKeyPath {
func partialKeyPaths() -> [T] {
return self.filter(into: [PartialKeyPath]()) 
{ result, keyPath in
if let k = keyPath as? PartialKeyPath {
result.append(k)
}
}
}
} 

To what end?

What we sorely lack in Swift is a way to (failably) init an object from a set 
of keypaths and values without tons of boilerplate and/or resorting to using 
string keys etc. 

Worse, right now there is no way to make a copy of an object/struct while 
mutating it only at one or two keypaths without writing yet more boilerplatey 
init methods.

Heck, right now, keypaths can be used for initializing neither immutable 
instance constants, nor mutable instance variables that lack default 
initializers. E.g.: self[keyPath: \Foo.bar] = “baz” fails to compile inside an 
init method, because the property is not initialized yet. Gee. 

Towards Type-Safe Instance Composition Patterns:

In a type-safe language we can’t have ECMA6-style destructuring 
(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)...
 and please don’t accuse me of wanting it.

All I want is some sugar that makes the Swift compiler infer more different 
kinds of convenience init methods. Something like:

struct Foo {
let bar: String
let baz: String
let leadScientist: QuantumPhysicist
let labTech: TeleporterTester
}

let fooMarch = Foo(bar: “asdf”, baz: “qwer”, leadScientist: 
QuantumPhysicist(“Alice”), labTech: TeleporterTester(“Bob”))

let fooApril = Foo(copy: fooMarch, overwriting: Property(\.labTech, 
TeleporterTester(“Charlie”)) 

... with “overwriting” taking 0 or more variadic arguments.

This allows easily, concisely composing an immutable instance of a type out of 
various components of other immutable instances of a type. I think this is an 
extremely powerful pattern, and many times I wish that I had it. 

In the absence of this, devs are prone to just use mutable instance vars 
instead of using immutable instance constants, just so they don’t have to do a 
whole member-wise initializer every time they want to just change one property.

Just my $0.02.

If there is already a way to use these things like that, then I want to know it.

As for “why would this really be useful”, “what are the real-world benefits”, 
etc. ... I feel like if you really have to ask this, then it’s not because you 
actually cannot see the obvious benefits—it’s because you hate America. 

~ Jon Gilbert

> On Aug 23, 2017, at 23:19, Logan Shire via swift-evolution 
>  wrote:
> 
> Hey folks! 
> 
> Recently I’ve been working on a small library which leverages the Swift 4 
> Codable protocol
> and KeyPaths to provide a Swift-y interface to CoreData. (It maps back and 
> forth between
> native, immutable Swift structs and NSManagedObjects). In doing so I found a 
> couple of 
> frustrating limitations to the KeyPath API. Firstly, KeyPath does not provide 
> the name of the 
> property on the type it indexes. For example, if I have a struct:
> 
> 
> struct Person {
>let firstName: String
>let lastName: String
> }
> 
> let keyPath = \Person.firstName
> 
> 
> But once I have a keyPath, I can’t actually figure out what property it 
> accesses.
> So, I wind up having to 

Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Joe Groff via swift-evolution
> On Aug 25, 2017, at 3:54 PM, Logan Shire  wrote:
> 
> How would you feel about wrapping the existing functions on 
> _KVOKeyPathBridgeMachinery:
> 
> @nonobjc fileprivate static func _bridgeKeyPath(_ keyPath:AnyKeyPath) -> 
> String
> @nonobjc fileprivate static func _bridgeKeyPath(_ keyPath:String?) -> 
> AnyKeyPath?
> 
> In extensions on String and AnyKeyPath respectively to instantiate strings 
> from KeyPaths and KeyPaths from Strings?

Those functions are designed for Cocoa interop only. They're not going to 
produce results that make sense for all Swift key paths.


-Joe

> https://github.com/apple/swift/blob/c5ff1f2cac8da6a14330f4b033b94c7c926d2126/stdlib/public/SDK/Foundation/NSObject.swift#L84
>  
> 
> 
> On Fri, Aug 25, 2017 at 11:43 AM Joe Groff  > wrote:
> 
> > On Aug 23, 2017, at 11:18 PM, Logan Shire via swift-evolution 
> > > wrote:
> >
> > Hey folks!
> >
> > Recently I’ve been working on a small library which leverages the Swift 4 
> > Codable protocol
> > and KeyPaths to provide a Swift-y interface to CoreData. (It maps back and 
> > forth between
> > native, immutable Swift structs and NSManagedObjects). In doing so I found 
> > a couple of
> > frustrating limitations to the KeyPath API. Firstly, KeyPath does not 
> > provide the name of the
> > property on the type it indexes. For example, if I have a struct:
> >
> >
> > struct Person {
> >let firstName: String
> >let lastName: String
> > }
> >
> > let keyPath = \Person.firstName
> >
> >
> > But once I have a keyPath, I can’t actually figure out what property it 
> > accesses.
> > So, I wind up having to make a wrapper:
> >
> >
> > struct Attribute {
> >let keyPath: AnyKeyPath
> >let propertyName: String
> > }
> >
> > let firstNameAttribute = Attribute(keyPath: \Person.firstName, 
> > propertyName: “firstName”)
> >
> >
> > This forces me to write out the property name myself as a string which is 
> > very error prone.
> > All I want is to be able to access:
> >
> >
> > keyPath.propertyName // “firstName”
> >
> >
> > It would also be nice if we provided the full path as a string as well:
> >
> >
> > keyPath.fullPath // “Person.firstName"
> >
> >
> > Also, if I want to get all of the attributes from a given Swift type, my 
> > options are to try to hack
> > something together with Mirrors, or forcing the type to declare a function 
> > / computed property
> > returning an array of all of its key path / property name pairings. I would 
> > really like to be able to
> > retrieve a type-erased array of any type’s key paths with:
> >
> >
> > let person = Person(firstName: “John”, lastName: “Doe”)
> > let keyPaths = Person.keyPaths
> > let firstNameKeyPath = keyPaths.first { $0.propertyName = “firstName” } as! 
> > KeyPath
> > let firstName = person[keypath: firstNameKeyPath] // “John"
> >
> >
> > And finally, without straying too far into Objective-C land, it would be 
> > nice if we could initialize key paths
> > with a throwing initializer.
> >
> >
> > let keyPath = try Person.keyPath(“firstName”) // KeyPath 
> > type erased to AnyKeyPath
> > let keyPath = AnyKeyPath(“Person.firstName”)
> >
> >
> > Let me know what you think about any / all of these suggestions!
> 
> These would all be great additional features to eventually add to key paths. 
> I think reflection mechanisms centered on key paths like what you describe 
> would be a superior replacement for most of what Mirror attempts to provide.
> 
> -Joe

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Logan Shire via swift-evolution
How would you feel about wrapping the existing functions
on _KVOKeyPathBridgeMachinery:

@nonobjc fileprivate static func _bridgeKeyPath(_ keyPath:AnyKeyPath) ->
String
@nonobjc fileprivate static func _bridgeKeyPath(_ keyPath:String?) ->
AnyKeyPath?

In extensions on String and AnyKeyPath respectively to instantiate strings
from KeyPaths and KeyPaths from Strings?

https://github.com/apple/swift/blob/c5ff1f2cac8da6a14330f4b033b94c7c926d2126/stdlib/public/SDK/Foundation/NSObject.swift#L84

On Fri, Aug 25, 2017 at 11:43 AM Joe Groff  wrote:

>
> > On Aug 23, 2017, at 11:18 PM, Logan Shire via swift-evolution <
> swift-evolution@swift.org> wrote:
> >
> > Hey folks!
> >
> > Recently I’ve been working on a small library which leverages the Swift
> 4 Codable protocol
> > and KeyPaths to provide a Swift-y interface to CoreData. (It maps back
> and forth between
> > native, immutable Swift structs and NSManagedObjects). In doing so I
> found a couple of
> > frustrating limitations to the KeyPath API. Firstly, KeyPath does not
> provide the name of the
> > property on the type it indexes. For example, if I have a struct:
> >
> >
> > struct Person {
> >let firstName: String
> >let lastName: String
> > }
> >
> > let keyPath = \Person.firstName
> >
> >
> > But once I have a keyPath, I can’t actually figure out what property it
> accesses.
> > So, I wind up having to make a wrapper:
> >
> >
> > struct Attribute {
> >let keyPath: AnyKeyPath
> >let propertyName: String
> > }
> >
> > let firstNameAttribute = Attribute(keyPath: \Person.firstName,
> propertyName: “firstName”)
> >
> >
> > This forces me to write out the property name myself as a string which
> is very error prone.
> > All I want is to be able to access:
> >
> >
> > keyPath.propertyName // “firstName”
> >
> >
> > It would also be nice if we provided the full path as a string as well:
> >
> >
> > keyPath.fullPath // “Person.firstName"
> >
> >
> > Also, if I want to get all of the attributes from a given Swift type, my
> options are to try to hack
> > something together with Mirrors, or forcing the type to declare a
> function / computed property
> > returning an array of all of its key path / property name pairings. I
> would really like to be able to
> > retrieve a type-erased array of any type’s key paths with:
> >
> >
> > let person = Person(firstName: “John”, lastName: “Doe”)
> > let keyPaths = Person.keyPaths
> > let firstNameKeyPath = keyPaths.first { $0.propertyName = “firstName” }
> as! KeyPath
> > let firstName = person[keypath: firstNameKeyPath] // “John"
> >
> >
> > And finally, without straying too far into Objective-C land, it would be
> nice if we could initialize key paths
> > with a throwing initializer.
> >
> >
> > let keyPath = try Person.keyPath(“firstName”) // KeyPath
> type erased to AnyKeyPath
> > let keyPath = AnyKeyPath(“Person.firstName”)
> >
> >
> > Let me know what you think about any / all of these suggestions!
>
> These would all be great additional features to eventually add to key
> paths. I think reflection mechanisms centered on key paths like what you
> describe would be a superior replacement for most of what Mirror attempts
> to provide.
>
> -Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Joe Groff via swift-evolution

> On Aug 25, 2017, at 1:45 PM, Eagle Offshore  wrote:
> 
> 
>> On Aug 25, 2017, at 1:35 PM, Joe Groff > > wrote:
>> 
>> What do you mean exactly by traits? That's an overloaded term.
> 
> 
> http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf 
> 
> 
> Like PHP or Pharo traits.  Like a protocol that has a default implementation 
> that comes along with it.

OK. Swift already has protocol extensions, which let you provide default 
implementations alongside protocols.

-Joe

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Joe Groff via swift-evolution

> On Aug 25, 2017, at 1:27 PM, Eagle Offshore  wrote:
> 
> Reflection mechanisms in general would let one make things like Codable and 
> Keypaths simply a library capability/protocol extension rather than the 
> special case one trick pony it is now.
> 
> More than any other feature discussed, full access to meta data such as 
> memory layouts is the thing I most miss from Objective C.  
> 
> Given reflection/introspection, you can build KVC, Codable, and any number of 
> other clever features yourself if you are so inclined.

Runtime information about type layouts exists in the Swift runtime, and there 
are many ways we can choose to expose it to programmers. There needs to be some 
stable runtime-provided interface at the bottom, and KeyPath has some 
advantages over ObjC-style string-based KVC as that foundation—they're strongly 
typed, and they don't need to depend on string descriptions of everything being 
embedded in the binary, which might be undesirable for secrecy reasons. We 
could allow a type to vend a collection of key paths to all of its fields 
without having to fully expose source-level details of what those fields are 
named and how they're laid out, which has been a shortcoming of ObjC's approach 
for a lot of secrecy-sensitive clients.

> Oh, and traits - want traits for composability.


What do you mean exactly by traits? That's an overloaded term.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Eagle Offshore via swift-evolution
Reflection mechanisms in general would let one make things like Codable and 
Keypaths simply a library capability/protocol extension rather than the special 
case one trick pony it is now.

More than any other feature discussed, full access to meta data such as memory 
layouts is the thing I most miss from Objective C.  

Given reflection/introspection, you can build KVC, Codable, and any number of 
other clever features yourself if you are so inclined.

Oh, and traits - want traits for composability.

> On Aug 25, 2017, at 11:43 AM, Joe Groff via swift-evolution 
>  wrote:
> 
> These would all be great additional features to eventually add to key paths. 
> I think reflection mechanisms centered on key paths like what you describe 
> would be a superior replacement for most of what Mirror attempts to provide.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] Improving KeyPath

2017-08-25 Thread Joe Groff via swift-evolution

> On Aug 23, 2017, at 11:18 PM, Logan Shire via swift-evolution 
>  wrote:
> 
> Hey folks! 
> 
> Recently I’ve been working on a small library which leverages the Swift 4 
> Codable protocol
> and KeyPaths to provide a Swift-y interface to CoreData. (It maps back and 
> forth between
> native, immutable Swift structs and NSManagedObjects). In doing so I found a 
> couple of 
> frustrating limitations to the KeyPath API. Firstly, KeyPath does not provide 
> the name of the 
> property on the type it indexes. For example, if I have a struct:
> 
> 
> struct Person {
>let firstName: String
>let lastName: String
> }
> 
> let keyPath = \Person.firstName
> 
> 
> But once I have a keyPath, I can’t actually figure out what property it 
> accesses.
> So, I wind up having to make a wrapper:
> 
> 
> struct Attribute {
>let keyPath: AnyKeyPath
>let propertyName: String
> }
> 
> let firstNameAttribute = Attribute(keyPath: \Person.firstName, propertyName: 
> “firstName”)
> 
> 
> This forces me to write out the property name myself as a string which is 
> very error prone.
> All I want is to be able to access:
> 
> 
> keyPath.propertyName // “firstName”
> 
> 
> It would also be nice if we provided the full path as a string as well:
> 
> 
> keyPath.fullPath // “Person.firstName"
> 
> 
> Also, if I want to get all of the attributes from a given Swift type, my 
> options are to try to hack
> something together with Mirrors, or forcing the type to declare a function / 
> computed property
> returning an array of all of its key path / property name pairings. I would 
> really like to be able to 
> retrieve a type-erased array of any type’s key paths with:
> 
> 
> let person = Person(firstName: “John”, lastName: “Doe”)
> let keyPaths = Person.keyPaths
> let firstNameKeyPath = keyPaths.first { $0.propertyName = “firstName” } as! 
> KeyPath
> let firstName = person[keypath: firstNameKeyPath] // “John"
> 
> 
> And finally, without straying too far into Objective-C land, it would be nice 
> if we could initialize key paths
> with a throwing initializer.
> 
> 
> let keyPath = try Person.keyPath(“firstName”) // KeyPath type 
> erased to AnyKeyPath
> let keyPath = AnyKeyPath(“Person.firstName”)
> 
> 
> Let me know what you think about any / all of these suggestions!

These would all be great additional features to eventually add to key paths. I 
think reflection mechanisms centered on key paths like what you describe would 
be a superior replacement for most of what Mirror attempts to provide.

-Joe
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


[swift-evolution] [Pitch] Improving KeyPath

2017-08-24 Thread Logan Shire via swift-evolution
Hey folks! 

Recently I’ve been working on a small library which leverages the Swift 4 
Codable protocol
and KeyPaths to provide a Swift-y interface to CoreData. (It maps back and 
forth between
native, immutable Swift structs and NSManagedObjects). In doing so I found a 
couple of 
frustrating limitations to the KeyPath API. Firstly, KeyPath does not provide 
the name of the 
property on the type it indexes. For example, if I have a struct:


struct Person {
let firstName: String
let lastName: String
}

let keyPath = \Person.firstName


But once I have a keyPath, I can’t actually figure out what property it 
accesses.
So, I wind up having to make a wrapper:


struct Attribute {
let keyPath: AnyKeyPath
let propertyName: String
}

let firstNameAttribute = Attribute(keyPath: \Person.firstName, propertyName: 
“firstName”)


This forces me to write out the property name myself as a string which is very 
error prone.
All I want is to be able to access:


keyPath.propertyName // “firstName”


It would also be nice if we provided the full path as a string as well:


keyPath.fullPath // “Person.firstName"


Also, if I want to get all of the attributes from a given Swift type, my 
options are to try to hack
something together with Mirrors, or forcing the type to declare a function / 
computed property
returning an array of all of its key path / property name pairings. I would 
really like to be able to 
retrieve a type-erased array of any type’s key paths with:


let person = Person(firstName: “John”, lastName: “Doe”)
let keyPaths = Person.keyPaths
let firstNameKeyPath = keyPaths.first { $0.propertyName = “firstName” } as! 
KeyPath
let firstName = person[keypath: firstNameKeyPath] // “John"


And finally, without straying too far into Objective-C land, it would be nice 
if we could initialize key paths
with a throwing initializer.


let keyPath = try Person.keyPath(“firstName”) // KeyPath type 
erased to AnyKeyPath
let keyPath = AnyKeyPath(“Person.firstName”)


Let me know what you think about any / all of these suggestions!


Thanks,
Logan


___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution