Sure, but you could just as easily call a keypath “a thing that gives you read+write access to one of an object’s members”. Sounds like an inout function to me. The difference being that in general an inout function *can* do a lot more than just give access to a single member of a base object. But a keypath is still just a (constrained) inout function.
> On Jul 11, 2017, at 9:14 PM, Dave Abrahams via swift-evolution > <[email protected]> wrote: > > >> on Tue Jul 11 2017, Robert Bennett <[email protected]> wrote: >> >> Er, yes, I now realize I diverged into two different keypaths-as-functions >> ideas there. >> >> 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. >> >> 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. >> >> 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). > > Because they're parameterized by a base object and a key, and (in > general) they're writable, they're semantically very much like > subscripts. > >>> On Jul 11, 2017, at 7:56 PM, Karl Wagner <[email protected]> wrote: >>> >>> >>>> On 12. Jul 2017, at 01:20, Robert Bennett >>>> <[email protected] >>>> <mailto:[email protected]>> 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 >>>>> <[email protected] >>>>> <mailto:[email protected]>> wrote: >>>>> >>>>> >>>>>> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution >>>>>> <[email protected] >>>>>> <mailto:[email protected]>> >>>>>> 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 >>>>>>> <[email protected] <mailto:[email protected]>> 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 >>>>>>> [email protected] >> <mailto:[email protected]> >>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> [email protected] >> <mailto:[email protected]> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>> >>> >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] >> https://lists.swift.org/mailman/listinfo/swift-evolution >> > > -- > -Dave > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
