[swift-evolution] Pitch: Support for map and flatMap with smart key paths

Susan Cheng susan.doggie at gmail.com
Wed Jun 7 23:34:43 CDT 2017


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>:

> 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> 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
>
> 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> 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> 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> 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> wrote:
>
> 💯
>
> On Jun 7, 2017, at 10:35 AM, 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
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170608/268c00fd/attachment.html>


More information about the swift-evolution mailing list