[swift-evolution] Pitch: Support for map and flatMap with smart key paths
Karl Wagner
razielim at gmail.com
Fri Jun 9 19:16:23 CDT 2017
> On 7. Jun 2017, at 19:35, Adam Sharp via swift-evolution <swift-evolution at 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 at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170610/6ef8d25c/attachment.html>
More information about the swift-evolution
mailing list