> 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

Reply via email to