[swift-evolution] Pitch: Support for map and flatMap with smart key paths
Paul Cantrell
cantrell at pobox.com
Wed Jun 7 23:40:45 CDT 2017
Ah, quite ingenious, Susan!
And you made me realize that the only reason my no-operator-needed code didn’t compile is that I had keypath instead of keyPath. So it was indeed “Paul needs to go to sleep now” option, which I will now do. 🙄
P
> On Jun 7, 2017, at 11:34 PM, Susan Cheng <susan.doggie at gmail.com> wrote:
>
> this work,
>
> prefix operator *
>
> prefix func *<Root, Value>(keyPath: KeyPath<Root, Value>) -> (Root) -> Value {
> return { $0[keyPath: keyPath] }
> }
>
> ["Hello, World"].map(*\String.count) // [12]
>
>
> 2017-06-08 12:19 GMT+08:00 Paul Cantrell via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
> It should be possible to achieve Ruby-like generality in Swift with a protocol for “thing that can converted to a transform function.” That wouldn’t need a special & operator.
>
> Here’s a sketch. This sketch doesn’t compile — maybe not enough of Swift 4 is there yet for it to work, or maybe I am missing something obvious and need to go to sleep now — but it’s close enough to suggest the approach:
>
> public protocol TransformConvertible { // or whatever you want to call it
> associatedtype From
> associatedtype To
>
> var transform: (From) -> To { get }
> }
>
> extension KeyPath: TransformConvertible {
> public typealias From = Root
> public typealias To = Value
>
> public var transform: (Root) -> Value {
> return { $0[keypath: self] }
> }
> }
>
> extension Sequence {
> public func map<T, U>(_ transformSource: U) -> [T]
> where U: TransformConvertible,
> U.From == Element,
> U.To == T {
> return map(transformSource.transform)
> }
> }
>
> This seems a bit more ambitious, perhaps not suitable for this round of Swift evolution work. But I throw it out there at least to show that supporting people.map(\.firstName) today would not preclude a generic keypath → function mechanism in the future:
>
> A flavor of map that accepts a keypath today could be generalized to accept TransformConvertible in the future without breaking existing code.
> When calling a function that doesn’t know how to work with TransformConvertible, you could use (Foo.bar).transform, no special operator needed.
>
> Cheers,
>
> Paul
>
> P.S. Largely irrelevant Ruby aside: Ruby’s & is not a free-floating operator, but part of the method invocation syntax indicating that the following arg should be treated as a block. Ruby calls a to_proc method on whatever is in that position. Symbol implements to_proc by returning a lambda that calls the method named by the symbol on the lambda’s first arg. Very much the duck-typed version of TransformConvertible above.
>
>
>> On Jun 7, 2017, at 10:21 PM, Stephen Celis via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>
>> -1
>>
>> A -1 from me may be surprising. I'm excited about key path composition and generic solutions, e.g. this experiment with lenses: https://twitter.com/stephencelis/status/863916921577758721 <https://twitter.com/stephencelis/status/863916921577758721>
>>
>> But I'd prefer a reusable solution for converting key paths into functions.
>>
>> Heaven help me for this Rubyism, but a prefix "&" operator (or, maybe better yet, some implicit mechanism) could convert a key-path to a function that passes a root value to a key path...
>>
>> people.map(&\.firstName)
>>
>> This way any function that takes a transformation from "whole" to "part" could take a key path. Requiring an overload per instance is less flexible.
>>
>> Stephen
>>
>>> On Jun 7, 2017, at 10:58 PM, Tony Allevato via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>
>>> +1, I really like this. It would also align nicely with the method type flattening in SE-0042 (once it gets implemented), because passing keypaths (i.e., unbound property references) and unbound parameterless method references to map/flatMap would look nearly the same:
>>>
>>> ```
>>> struct Person {
>>> let firstName: String
>>> let lastName: String
>>> func fullName() -> String { return "\(firstName) \(lastName)" }
>>> }
>>>
>>> let people: [Person]
>>> let firstNames = people.map(\.firstName)
>>> let fullNames = people.map(Person.fullName) // because after SE-0042, this will be (Person) -> String, not (Person) -> () -> String
>>> ```
>>>
>>> Especially if there's a move in the future to also use \. to denote unbound methods references, which was discussed during the keypath reviews. (Even with that, I believe it would be more work though to get rid of the explicit type name in the function case.)
>>>
>>>
>>> On Wed, Jun 7, 2017 at 6:11 PM Xiaodi Wu via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> +1. Would think that all variants should exist on Optional too unless it would be harmful.
>>> On Wed, Jun 7, 2017 at 20:13 Michael J LeHew Jr via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> This is a great idea, and ought to be easy enough to bring forward! +1 from me!
>>>
>>> -Michael
>>>
>>>> On Jun 7, 2017, at 11:18 AM, Matt Diephouse via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>
>>>> 💯
>>>>
>>>>> On Jun 7, 2017, at 10:35 AM, Adam Sharp via swift-evolution <swift-evolution at swift.org <mailto: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 <mailto:swift-evolution at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>
>>>> _______________________________________________
>>>> 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>
>>>
>>> _______________________________________________
>>> 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>
>>> _______________________________________________
>>> 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>
>>> _______________________________________________
>>> 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>
>>
>> _______________________________________________
>> 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>
>
>
> _______________________________________________
> 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/20170607/262133cb/attachment.html>
More information about the swift-evolution
mailing list