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

Reply via email to