<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">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.</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">public</span> <span style="color: #323e7d" class="">protocol</span> TransformConvertible { // or whatever you want to call it</div><div style="margin: 0px; line-height: normal; color: rgb(50, 62, 125);" class=""><span style="color: #000000" class=""> </span>associatedtype<span style="color: #000000" class=""> From</span></div><div style="margin: 0px; line-height: normal; color: rgb(50, 62, 125);" class=""><span style="color: #000000" class=""> </span>associatedtype<span style="color: #000000" class=""> To</span></div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">var</span> transform: (<span style="color: #587ea8" class="">From</span>) -> <span style="color: #587ea8" class="">To</span> { <span style="color: #323e7d" class="">get</span> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; font-size: 12px; line-height: normal; font-family: Helvetica; min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; color: rgb(88, 126, 168);" class=""><span style="color: #000000" class=""> </span><span style="color: #323e7d" class="">extension</span><span style="color: #000000" class=""> </span>KeyPath<span style="color: #000000" class="">: </span>TransformConvertible<span style="color: #000000" class=""> {</span></div><div style="margin: 0px; line-height: normal; color: rgb(50, 62, 125);" class=""><span style="color: #000000" class=""> </span>public<span style="color: #000000" class=""> </span>typealias<span style="color: #000000" class=""> From = </span><span style="color: #587ea8" class="">Root</span></div><div style="margin: 0px; line-height: normal; color: rgb(50, 62, 125);" class=""><span style="color: #000000" class=""> </span>public<span style="color: #000000" class=""> </span>typealias<span style="color: #000000" class=""> To = </span><span style="color: #587ea8" class="">Value</span></div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">public</span> <span style="color: #323e7d" class="">var</span> transform: (<span style="color: #587ea8" class="">Root</span>) -> <span style="color: #587ea8" class="">Value</span> {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">return</span> { $0[keypath: <span style="color: #323e7d" class="">self</span>] }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; font-size: 12px; line-height: normal; font-family: Helvetica; min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; color: rgb(50, 62, 125);" class=""><span style="color: #000000" class=""> </span>extension<span style="color: #000000" class=""> </span><span style="color: #587ea8" class="">Sequence</span><span style="color: #000000" class=""> {</span></div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">public</span> <span style="color: #323e7d" class="">func</span> map<T, U>(<span style="color: #323e7d" class="">_</span> transformSource: <span style="color: #587ea8" class="">U</span>) -> [<span style="color: #587ea8" class="">T</span>]</div><div style="margin: 0px; line-height: normal; color: rgb(88, 126, 168);" class=""><span style="color: #000000" class=""> </span><span style="color: #323e7d" class="">where</span><span style="color: #000000" class=""> </span>U<span style="color: #000000" class="">: </span>TransformConvertible<span style="color: #000000" class="">,</span></div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: rgb(88, 126, 168);" class="">U</span>.<span style="color: #587ea8" class="">From</span> == <span style="color: #587ea8" class="">Element</span>,</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: rgb(88, 126, 168);" class="">U</span>.<span style="color: #587ea8" class="">To</span> == <font color="#587ea8" class="">T </font>{</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #323e7d" class="">return</span> <span style="color: #587ea8" class="">map</span>(transformSource.<span style="color: #587ea8" class="">transform</span>)</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div class=""><br class=""></div></div></div><div class="">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 <span style="font-family: Menlo; font-size: 11px; background-color: rgb(255, 255, 255);" class="">people.map(\.firstName)</span> today <b class="">would not preclude</b> a generic keypath → function mechanism in the future:</div><div class=""><br class=""></div><div class=""><ul class="MailOutline"><li class="">A flavor of map that accepts a keypath today could be generalized to accept TransformConvertible in the future without breaking existing code.</li><li class="">When calling a function that doesn’t know how to work with TransformConvertible, you could use (Foo.bar).transform, no special operator needed.</li></ul></div><div class=""><br class=""></div><div class="">Cheers,</div><div class=""><br class=""></div><div class="">Paul</div><div class=""><br class=""></div><div class=""><div class="">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.</div></div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On Jun 7, 2017, at 10:21 PM, Stephen Celis via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">-1<br class=""><br class="">A -1 from me may be surprising. I'm excited about key path composition and generic solutions, e.g. this experiment with lenses: <a href="https://twitter.com/stephencelis/status/863916921577758721" class="">https://twitter.com/stephencelis/status/863916921577758721</a><br class=""><br class="">But I'd prefer a reusable solution for converting key paths into functions.<br class=""><br class="">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...<br class=""><br class=""> people.map(&\.firstName)<br class=""><br class="">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.<br class=""><br class="">Stephen<br class=""><br class=""><blockquote type="cite" class="">On Jun 7, 2017, at 10:58 PM, Tony Allevato via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">+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:<br class=""><br class="">```<br class="">struct Person {<br class=""> let firstName: String<br class=""> let lastName: String<br class=""> func fullName() -> String { return "\(firstName) \(lastName)" }<br class="">}<br class=""><br class="">let people: [Person]<br class="">let firstNames = people.map(\.firstName)<br class="">let fullNames = people.map(Person.fullName) // because after SE-0042, this will be (Person) -> String, not (Person) -> () -> String<br class="">```<br class=""><br class="">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.)<br class=""><br class=""><br class="">On Wed, Jun 7, 2017 at 6:11 PM Xiaodi Wu via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class="">+1. Would think that all variants should exist on Optional too unless it would be harmful.<br class="">On Wed, Jun 7, 2017 at 20:13 Michael J LeHew Jr via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class="">This is a great idea, and ought to be easy enough to bring forward! +1 from me!<br class=""><br class="">-Michael<br class=""><br class=""><blockquote type="cite" class="">On Jun 7, 2017, at 11:18 AM, Matt Diephouse via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">💯<br class=""><br class=""><blockquote type="cite" class="">On Jun 7, 2017, at 10:35 AM, Adam Sharp via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">The new smart key path feature is really lovely, and feels like a great addition to Swift.<br class=""><br class="">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:<br class=""><br class=""> let managers = flatOrganisation.managers<br class=""> let allEmployees = Set(managers.flatMap(\.directReports))<br class=""> let employeeNames = Set(allEmployees.map(\.name))<br class=""><br class="">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).<br class=""><br class="">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!<br class=""><br class="">–Adam<br class=""><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></blockquote><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></blockquote><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class="">_______________________________________________<br class="">swift-evolution mailing list<br class="">swift-evolution@swift.org<br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class="">_______________________________________________<br class="">swift-evolution mailing list<br class="">swift-evolution@swift.org<br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></blockquote><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></div></blockquote></div><br class=""></body></html>