<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="">Er, yes, I now realize I diverged into two different keypaths-as-functions ideas there.<div class=""><br class=""></div><div class="">I think that the best implementation of deferred access is keypaths as callable first-class objects, like you (Karl) said. — although I wonder whether callability should be restricted to KeyPath, or instead, if the notion of a callable type should gain first-class language support.</div><div class=""><br class=""></div><div class="">If not possible, then a conversion sigil to make a KeyPath into a function. After that, giving KeyPath a function `apply` is probably next best.</div><div class=""><br class=""></div><div class="">I like the subscript idea the least because: I don’t like the look of the syntax, keypaths feel more function-y than subscript-y, and it diminishes the flexibility of keypaths (as this thread has revealed).<div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jul 11, 2017, at 7:56 PM, Karl Wagner <<a href="mailto:razielim@gmail.com" class="">razielim@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On 12. Jul 2017, at 01:20, Robert Bennett <<a href="mailto:rltbennett@icloud.com" class="">rltbennett@icloud.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Well, if they really are first-class deferred method calls or member accesses, then do seem pretty function-y after all. Then again, if they were meant to be functions, it seems like their design would reflect that – instead of being used like subscripts, they would be called like functions, and since new syntax had to be created either way, the fact that they *weren't* just made into callable objects seems to indicate that that was not the intent, although I’d have to go back and read the discussion to see exactly what was discussed. <br class=""></div></div></blockquote><div class=""><br class=""></div><div class="">I agree, and I suspect I’m not alone in disliking the subscript syntax.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">That said, I agree with Benjamin that having an `apply` method for KeyPath seems like the right way to make (or have made) keypaths work. keypath.apply(to: instance) (or keypath.evaluate(on:), or some other name that gets the idea across) reads just as nice as instance[keyPath: keypath] and has the added benefit of allowing collection.map(keypath.apply) at no cost. But it’s probably too late to even bother having a discussion about this, right?<br class=""></div></div></blockquote><div class=""><br class=""></div><div class="">I don’t think so. That’s why we have a beta. Real-world experience has shown that we would often like to erase a KeyPath and use it as if it were a closure. Maybe we want to pass a KeyPath as a parameter to a function such as “map", which accepts a closure, or we want to set a variable with a closure-type using a KeyPath. Those functions and stored properties don’t care about the special properties of KeyPaths (e.g. that they are Codable) - they only care that they are executable on a base object to produce a result. You can wrap the key-paths inside closures, but it’s cumbersome and some developers are asking for a shorthand to perform that erasure.</div><div class=""><br class=""></div><div class="">Personally, I would be in favour of making the erasure implicit and allowing KeyPaths to be invoked using function-syntax:</div><div class=""><br class=""></div></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Menlo" class="">// Invocation using function-syntax:</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div></div></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Menlo" class="">let _: Value = someClosure(parameter)</font></div></div><div class=""><div class=""><font face="Menlo" class="">let _: Value = someKeypath(parameter)</font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class="">let _: Value = { $0.something.anotherThing }(parameter)</font></div></div><div class=""><font face="Menlo" class="">let _: Value = (\MyObj.something.anotherThing)(parameter)</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">// Implicit erasure to closure-type:</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">class PrettyTableCell<T> {</font></div><div class=""><font face="Menlo" class=""> let titleFormatter: (T) -> String</font></div><div class=""><font face="Menlo" class="">}</font></div><div class=""><font face="Menlo" class="">let cell = PrettyTableCell<MyObj>()</font></div><div class=""><font face="Menlo" class="">cell.titleFormatter = \MyObj.something.name</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">let stuff = myObjects.map(\.something.anotherThing)</font></div><div class=""><br class=""></div></blockquote>- Karl<br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">On Jul 11, 2017, at 6:27 PM, Karl Wagner <<a href="mailto:razielim@gmail.com" class="">razielim@gmail.com</a>> wrote:<br class=""><br class=""><br class=""><blockquote type="cite" class="">On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">In general, I like the idea of making ordinary types callable (although curried functions already accomplish this to some extent), but I hesitate to bring this capability to keypaths because, well, they don’t really feel like functions; I think the point of them is that they work like subscripts, not functions. After all, before keypaths were added, there was already an easy to make a function that does what a keypath does (which makes me wonder whether keypaths were necessary in the first place, but that ship has sailed). The only reason to add callable support to keypaths is for use in map, which I don’t think justifies making them callable.<br class=""><br class="">Also, since I brought this up, I’d like to be proved wrong about keypaths – what use do they have that isn’t accomplished by the equivalent closure?<br class=""></blockquote><br class="">I can’t find a formal definition of a “keypath”, so let me explain how I think of them:<br class=""><br class="">Conceptually, I think I would define a KeyPath as a stateless, deferred function application with one unbound argument (the “base"). Anything you do with a KeyPath could be done with a closure of type (Base)->Value which captures all other arguments (e.g. subscript/function parameters). The major benefit that it has over a closure is identity (so you can put it in a dictionary or compare two keypaths), and that property that captures all of its parameters except the base, and that those parameters don’t have stateful side-effects. That makes it really handy for parallel execution and database predicates in ORMs.<br class=""><br class="">There’s also another benefit of KeyPaths: they are de-/serialisable. Again, since it captures all of its (stateless) parameters, it itself is stateless and can be transferred to persistent storage or over a network.<br class=""><br class="">You can actually see those constraints in the KeyPath proposal (which is part of what makes it such a great proposal, IMO): all captured parameters must be Hashable and Codable.<br class=""><br class="">But to come back to your point - in all other respects a KeyPath is conceptually identical to a closure of type (Base)->Value. It’s like a specially-annotated closure, where it’s special construction syntax lets us statically verify that it’s a stateless, deferred function applicable to an instance of the Base type.<br class=""><br class="">The KeyPath proposal said that eventually, the core team would like to be able to support arbitrary function calls in KeyPath expressions, too. For example, it’s "not fair” that \MyObject.firstFiveElements and \MyObject[3] are valid KeyPaths, but \MyObject.prefix(5) is not. It’s also expressible as (Base)->Value, so conceptually it’s also a KeyPath and can be serialised and whatnot.<br class=""><br class="">- Karl <br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class="">On Jul 11, 2017, at 2:28 PM, Benjamin Herzog via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">I still think using an operator for this conversation would neither increase readability nor transparency. I think my mail on Sunday was lost, so I paste the content here again. It referred to a suggestion to create a possibility for KeyPath to act as a function which would bring other benefits as well:<br class=""><br class="">In Scala you can implement an apply method which makes it possible to call an object just like a function. Example:<br class=""><br class="">case class Foo(x: Int) {<br class=""> def apply(y: Int) = x + y<br class="">}<br class=""><br class="">val foo = Foo(3)<br class="">val bar = foo(4) // 7<br class=""><br class="">That is similar to what you suggested to have a possibility to convert an object to a closure getting called. And I totally see the point for this! I think using a keyword or special name like apply is not a good idea because it's not obvious what it does and it also makes it possible to just call the method with its name: foo.apply(4).<br class=""><br class="">However, having a protocol is kinda hard because it's not possible to have a flexible parameter list. Maybe having a method without a name? Swift example:<br class=""><br class="">class Foo {<br class=""> var x: Int<br class=""> init(x: Int) { self.x = x }<br class=""><br class=""> func (y: Int) -> Int {<br class=""> return self.x + y<br class=""> }<br class="">}<br class=""><br class="">let foo = Foo(x: 3)<br class="">let bar = foo(y: 4) // 7<br class=""><br class="">I actually like that, would be like an anonymous function. It would also be possible to have multiple of those defined for one object (which would have to be unambiguous of course).<br class=""><br class="">So getting back to KeyPath, it could look like this:<br class=""><br class="">class KeyPath<Root, Value> {<br class=""> func (_ root: Root) -> Value {<br class=""> return root[keyPath: self]<br class=""> } <br class="">}<br class=""><br class="">I see that this would be a much bigger change and would not justify the syntactic sugar for map, flatMap, etc. But it would still be a nice addition to the Swift programming language, especially for KeyPath, transformers etc.<br class=""><br class="">What do you think?<br class=""><br class="">______________________<br class=""><br class="">Benjamin Herzog<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=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></blockquote>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></blockquote><br class=""></blockquote></div></div></blockquote></div><br class=""></div></div></blockquote></div><br class=""></div></div></body></html>