> On 7. Jun 2017, at 19:35, Adam Sharp via swift-evolution > <swift-evolution@swift.org> wrote: > > The new smart key path feature is really lovely, and feels like a great > addition to Swift. > > It seems like it might be straightforward to add overloads of `map` and > `flatMap` to the standard library to make use of the new functionality: > > let managers = flatOrganisation.managers > let allEmployees = Set(managers.flatMap(\.directReports)) > let employeeNames = Set(allEmployees.map(\.name)) > > This feels like a really natural way of working with key paths in a > functional style. It makes a lot of sense for collections, and possibly for > Optional too (although as far as I can see optional chaining is more or less > equivalent, and with more compact syntax). > > I’m hoping that this might be low-hanging fruit that could be considered for > the Swift 4 release. I’d be happy to have a go at writing a proposal if > there’s interest! > > –Adam > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
Working demo: struct VirtualKeyPath<Root, Value> { let block: (Root) -> Value func evaluate(on: Root) -> Value { return block(on) } } // If we could extend 'Any', this would be possible... //extension Any { // subscript(keyPath: VirtualKeyPath<Self, Value>) -> Value { // return keyPath.evaluate(on: self) // } //} extension KeyPath where Value: Collection { func map<T>(_ descendent: KeyPath<Value.Element, T>) -> VirtualKeyPath<Root, [T]> { return VirtualKeyPath<Root, [T]> { (obj: Root) -> [T] in return obj[keyPath: self].map { $0[keyPath: descendent] } } } } extension VirtualKeyPath where Value: Collection { func map<T>(_ descendent: KeyPath<Value.Element, T>) -> VirtualKeyPath<Root, [T]> { return VirtualKeyPath<Root, [T]> { (obj: Root) -> [T] in return self.evaluate(on: obj).map { $0[keyPath: descendent] } } } } struct Person { let name: String } struct Department { let people: [Person] } let nameLengths = (\Department.people).map(\.name).map(\.characters.count) let testObj = Department(people: [Person(name: "Alice"), Person(name: "Bob"), Person(name: "Claire"), Person(name: "David")]) kp.evaluate(on: testObj) // returns [5, 3, 6, 5] As far as making this kind of thing easier in the language is concerned, one thing I can think of is allowing another \ to end the key-path expression, rather than enclosing it with brackets. So: let nameLengths = (\Department.people).map(\.name).map(\.characters.count) Becomes: let nameLengths = \Department.people\.map(\.name).map(\.characters.count) And that’s it, I think. It’s quite nice as-is. - Karl
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution