> On 12. Jul 2017, at 01:20, Robert Bennett <rltbenn...@icloud.com> wrote: > > 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.
I agree, and I suspect I’m not alone in disliking the subscript syntax. > > 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? 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. Personally, I would be in favour of making the erasure implicit and allowing KeyPaths to be invoked using function-syntax: // Invocation using function-syntax: let _: Value = someClosure(parameter) let _: Value = someKeypath(parameter) let _: Value = { $0.something.anotherThing }(parameter) let _: Value = (\MyObj.something.anotherThing)(parameter) // Implicit erasure to closure-type: class PrettyTableCell<T> { let titleFormatter: (T) -> String } let cell = PrettyTableCell<MyObj>() cell.titleFormatter = \MyObj.something.name let stuff = myObjects.map(\.something.anotherThing) - Karl > >> On Jul 11, 2017, at 6:27 PM, Karl Wagner <razie...@gmail.com> wrote: >> >> >>> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution >>> <swift-evolution@swift.org> wrote: >>> >>> 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. >>> >>> 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? >> >> I can’t find a formal definition of a “keypath”, so let me explain how I >> think of them: >> >> 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. >> >> 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. >> >> 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. >> >> 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. >> >> 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. >> >> - Karl >> >>>> On Jul 11, 2017, at 2:28 PM, Benjamin Herzog via swift-evolution >>>> <swift-evolution@swift.org> wrote: >>>> >>>> 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: >>>> >>>> In Scala you can implement an apply method which makes it possible to call >>>> an object just like a function. Example: >>>> >>>> case class Foo(x: Int) { >>>> def apply(y: Int) = x + y >>>> } >>>> >>>> val foo = Foo(3) >>>> val bar = foo(4) // 7 >>>> >>>> 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). >>>> >>>> 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: >>>> >>>> class Foo { >>>> var x: Int >>>> init(x: Int) { self.x = x } >>>> >>>> func (y: Int) -> Int { >>>> return self.x + y >>>> } >>>> } >>>> >>>> let foo = Foo(x: 3) >>>> let bar = foo(y: 4) // 7 >>>> >>>> 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). >>>> >>>> So getting back to KeyPath, it could look like this: >>>> >>>> class KeyPath<Root, Value> { >>>> func (_ root: Root) -> Value { >>>> return root[keyPath: self] >>>> } >>>> } >>>> >>>> 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. >>>> >>>> What do you think? >>>> >>>> ______________________ >>>> >>>> Benjamin Herzog >>>> >>>> _______________________________________________ >>>> swift-evolution mailing list >>>> swift-evolution@swift.org >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> _______________________________________________ >>> swift-evolution mailing list >>> swift-evolution@swift.org >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution