Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-22 Thread Benjamin Herzog via swift-evolution
I also thought about adding a computed property or method to KeyPath to
get the callable behaviour, but it is really hard to distinguish between
the keypath and it's method, especially in the literal syntax. Adding a
separator like # would help but would bring the same new complexity like
a prefix operator. If we add something like this it should be there for
a broader use in my opinion.

After thinking a bit more about the topic I tend more to add callable
behaviour to instances via a keyword. Background is that I found
similarities to `init`.

`init` is a keyword in Swift which is called on the type name with given
parameters and always return `Self` (`init?` returns `Self?`). So I
think it would be consistent to add another keyword which enables the
same thing on an instance basis. Like `init` it could also be overloaded
and in this case even return different types.

My first proposal is `call` but this could be a bit too generic and
could also break existing code, so if you have better name suggestions
go ahead! :)

An example could look like this:

class ArrayWrapper {

private var elements: [Element]

// `init` as keyword is called on static level -> KeyPath(root: )
init(elements: Element...) { self.elements = elements }

// `call` as new keyword would make the object callable
call(index: Int) -> Element { return self.elements[index] }
}

let a = ArrayWrapper(elements: 1, 2, 3)
a(index: 1) // a can now also act as a function 2

In this case, `a` could also be used as a normal function taking an
`Int` as parameter and returning an `Int` (because `Element` is replaced
by `Int`) after calling.

func someFunction(f: (Int) -> Int) -> Int {
return f(1)
}
someFunction(f: a) // 2

In the same manner this could be implemented for KeyPath to
act as (Root) -> Value. It could also act as a setter lens by having one
parameter declared as `inout`.

What do you think about this?
__

Benjamin Herzog

On Mon, Jul 17, 2017, at 02:03 PM, Tino Heth via swift-evolution wrote:
> I hope for Swift meta-programming features evolving alongside reflection
> (both topics imho still need a complete concept):
> How about
> collection.map(MyObject.myProperty.get)
> or, to avoid possible ambiguity, something like
> collection.map(.myProperty#get)
> ?
> ___
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-17 Thread Tino Heth via swift-evolution
I hope for Swift meta-programming features evolving alongside reflection (both 
topics imho still need a complete concept):
How about
collection.map(MyObject.myProperty.get)
or, to avoid possible ambiguity, something like
collection.map(.myProperty#get)
?
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-17 Thread Elviro Rocca via swift-evolution
Subscripts in Swift in fact have always looked to me like a way to make objects 
"callable", where you use brackets instead of parentheses: the difference of 
course is the fact that, when used point-free, the object will be considered an 
instance and not a function.

In this sense we could theoretically make a KeyPath callable by adding 
subscripts; consider the following code:


extension KeyPath {
subscript (get root: Root) -> Value {
return root[keyPath: self]
}
}

struct Person {
var firstName: String
var lastName: String
}

let x = Person.init(firstName: "Foo", lastName: "Bar")

let foo = (\Person.firstName)[get: x] /// "Foo"


This is valid Swift code (Swift 4.0 snapshot 2017-07-13). In theory a possible 
evolution of this could be referencing the subscript function in a point-free 
way like we can already do with regular functions:


let callable1 = (\Person.firstName)[get:] /// this won't compile


For the setter part, because the other half of a KeyPath is essentially a 
function of type (Value, inout Root) -> (), we could theoretically considering 
this kind of subscript function:


extension KeyPath {
subscript (set value: Value, on root: inout Root) -> () { /// this 
won't compile
root[keyPath: self] = value
return ()
}
}

let callable2 = (\Person.firstName)[set:,on:] /// this won't compile


But we cannot write subscripts with inout parameters. I actually find the 
subscript path a very interesting one to consider, but there's still a lot of 
machinery that's missing.

I would prefer two standardized prefix operators - for extracting the "(Root) 
-> Value" and the "(Value,inout Root) -> ()" parts of a KeyPath - to be added 
to the standard library. This is exactly the case where custom operators make 
sense: repeated operations that should be expressed with minimum code noise.


Elviro

> Il giorno 16 lug 2017, alle ore 18:34, Benjamin Herzog via swift-evolution 
>  ha scritto:
> 
> If it would be possible to make objects callable, wouldn't that also go
> in the same direction as subscripts currently? One could also implement
> it with the syntax for callable functions (in this case anonymous -
> without a name). Instead of this:
> 
> subscript(index: Int) -> T
> 
> we could also write
> 
> func (_ index: Int) -> T
> 
> On the call side it would change from this:
> 
> list[3] to list(3)
> 
> I know that it's not necessary and not even better readable, but it goes
> in the same direction in my opinion and is worth considering. What do
> you think?
> 
> __
> 
> Benjamin Herzog
> 
> On Wed, Jul 12, 2017, at 10:21 PM, Dave Abrahams via swift-evolution
> wrote:
>> 
>> on Tue Jul 11 2017, Robert Bennett  wrote:
>> 
>>> Just realized that even inout functions don’t let you do
>>> member(object) = value. 
>> 
>> The other difference is that an inout function can't be used to get a
>> member from an immutable value, whereas a keypath/subscript/property
>> access can.
>> 
>> 
>> -- 
>> -Dave
>> ___
>> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-16 Thread Karl Wagner via swift-evolution

> On 12. Jul 2017, at 22:21, Dave Abrahams via swift-evolution 
>  wrote:
> 
> 
> on Tue Jul 11 2017, Robert Bennett  wrote:
> 
>> Just realized that even inout functions don’t let you do
>> member(object) = value. 
> 
> The other difference is that an inout function can't be used to get a
> member from an immutable value, whereas a keypath/subscript/property
> access can.
> 
> 
> -- 
> -Dave
> ___
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

Hmm… I forgot about setters.

I would still like some simple way to use a KeyPath where an equivalent closure 
would be expected (for getters AND setters). The operator approach looks like 
the best one, but I’d prefer a prefix operator, so…

\MyObj.something.name  // returns KeyPath

^\MyObj.something.name // returns (MyObj)->String
^myKeyPath // as above.

*\MyObj.something.name // returns (inout MyObj, String)->Void
*myKeyPath // as above.


Where those operators could, either now or later, be backed by some kind of 
protocol (similar to Equatable/Comparable) to express general-purpose callable 
objects.

But I don’t like how cryptic it all looks.

- Karl___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-16 Thread Benjamin Herzog via swift-evolution
If it would be possible to make objects callable, wouldn't that also go
in the same direction as subscripts currently? One could also implement
it with the syntax for callable functions (in this case anonymous -
without a name). Instead of this:

subscript(index: Int) -> T

we could also write

func (_ index: Int) -> T

On the call side it would change from this:

list[3] to list(3)

I know that it's not necessary and not even better readable, but it goes
in the same direction in my opinion and is worth considering. What do
you think?

__

Benjamin Herzog

On Wed, Jul 12, 2017, at 10:21 PM, Dave Abrahams via swift-evolution
wrote:
> 
> on Tue Jul 11 2017, Robert Bennett  wrote:
> 
> > Just realized that even inout functions don’t let you do
> > member(object) = value. 
> 
> The other difference is that an inout function can't be used to get a
> member from an immutable value, whereas a keypath/subscript/property
> access can.
> 
> 
> -- 
> -Dave
> ___
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-12 Thread Dave Abrahams via swift-evolution

on Tue Jul 11 2017, Robert Bennett  wrote:

> Just realized that even inout functions don’t let you do
> member(object) = value. 

The other difference is that an inout function can't be used to get a
member from an immutable value, whereas a keypath/subscript/property
access can.


-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
Just realized that even inout functions don’t let you do member(object) = 
value. So you’re right, the write access does make keypaths more like 
subscripts. But when restricted to read-only access, they are identical to 
(non-inout) functions and so it does seem natural to be able to use them as 
such for the purpose of map and similar functions.

You’ve convinced me that the sigil method is probably preferable... Since write 
access does make keypaths more like subscripts, the proper way to convert 
keypaths to functions is probably an explicit piece of punctuation.

> On Jul 11, 2017, at 9:23 PM, Robert Bennett via swift-evolution 
>  wrote:
> 
> 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 
>>  wrote:
>> 
>> 
>>> on Tue Jul 11 2017, Robert Bennett  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  wrote:
 
 
> On 12. Jul 2017, at 01:20, Robert Bennett
>  > 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 {
   let titleFormatter: (T) -> String
 }
 let cell= PrettyTableCell()
 cell.titleFormatter = \MyObj.something.name
 
 let stuff = myObjects.map(\.something.anotherThing)
 
 - Karl
 
> 
>> On Jul 11, 2017, at 6:27 PM, Karl Wagner
>> > 

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
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 
>  wrote:
> 
> 
>> on Tue Jul 11 2017, Robert Bennett  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  wrote:
>>> 
>>> 
 On 12. Jul 2017, at 01:20, Robert Bennett
 >>> > 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 {
>>>let titleFormatter: (T) -> String
>>> }
>>> let cell= PrettyTableCell()
>>> cell.titleFormatter = \MyObj.something.name
>>> 
>>> let stuff = myObjects.map(\.something.anotherThing)
>>> 
>>> - Karl
>>> 
 
> On Jul 11, 2017, at 6:27 PM, Karl Wagner
>  > wrote:
> 
> 
>> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution
>> > >
>> 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).

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Dave Abrahams via swift-evolution

on Tue Jul 11 2017, Robert Bennett  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  wrote:
>> 
>> 
>>> On 12. Jul 2017, at 01:20, Robert Bennett
>>> >> > 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 {
>> let titleFormatter: (T) -> String
>> }
>> let cell= PrettyTableCell()
>> cell.titleFormatter = \MyObj.something.name
>> 
>> let stuff = myObjects.map(\.something.anotherThing)
>> 
>> - Karl
>> 
>>> 
 On Jul 11, 2017, at 6:27 PM, Karl Wagner
 >>> > wrote:
 
 
> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution
>  >
> 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
 “ba

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
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).

> On Jul 11, 2017, at 7:56 PM, Karl Wagner  wrote:
> 
> 
>> On 12. Jul 2017, at 01:20, Robert Bennett > > 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 {
> let titleFormatter: (T) -> String
> }
> let cell= PrettyTableCell()
> cell.titleFormatter = \MyObj.something.name
> 
> let stuff = myObjects.map(\.something.anotherThing)
> 
> - Karl
> 
>> 
>>> On Jul 11, 2017, at 6:27 PM, Karl Wagner >> > wrote:
>>> 
>>> 
 On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution 
 mailto: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 tha

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Karl Wagner via swift-evolution

> On 12. Jul 2017, at 01:20, Robert Bennett  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 {
let titleFormatter: (T) -> String
}
let cell= PrettyTableCell()
cell.titleFormatter = \MyObj.something.name

let stuff = myObjects.map(\.something.anotherThing)

- Karl

> 
>> On Jul 11, 2017, at 6:27 PM, Karl Wagner  wrote:
>> 
>> 
>>> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution 
>>>  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 sai

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
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. 

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?

> On Jul 11, 2017, at 6:27 PM, Karl Wagner  wrote:
> 
> 
>> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution 
>>  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 
>>>  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 beca

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Karl Wagner via swift-evolution

> On 11. Jul 2017, at 21:01, Robert Bennett via swift-evolution 
>  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 
>>  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 {
>>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-evoluti

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
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?

> On Jul 11, 2017, at 2:28 PM, Benjamin Herzog via swift-evolution 
>  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 {
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Benjamin Herzog via swift-evolution
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 {
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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
This is also the case for $0, although I suppose that since $0 is only valid 
inside a closure, LLDB has some context with which to disambiguate Swift’s $0 
from LLDB’s, context it wouldn’t have with $keyPath. That said, there are 
probably solutions to this, like requiring a backslash before a Swift dollar 
sign to disambiguate from an LLDB dollar sign. Or something else. Designing the 
language with constraints imposed by the debugger seems unduly restrictive, 
though – surely the debugger should be subservient to the language, not the 
other way around.

> On Jul 11, 2017, at 12:44 PM, BJ Homer  wrote:
> 
> ‘$' as a prefix is reserved for use by the debugger, so it cannot be used as 
> a conversion operator here.
> 
> -BJ
> 
>> On Jul 11, 2017, at 10:12 AM, Robert Bennett via swift-evolution 
>>  wrote:
>> 
>> It seems that there is some consensus that the proper way to achieve this is 
>> not to overload map et al. but to provide a way to convert KeyPath to a 
>> function. I agree – not only is this “cheaper”, as overloads of these 
>> functions will not need to be written, but it’s also more general and may 
>> prove useful in a context that we currently don’t foresee.
>> 
>> This being the case, I’ll repeat my proposal that the optimal way to achieve 
>> this is to make $ the conversion “operator” (although it need not, and 
>> probably should not, be a full-fledged operator), so that $keyPath –> { 
>> $0[keyPath: keyPath] }
>> 
>>> On Jul 11, 2017, at 11:13 AM, Elviro Rocca via swift-evolution 
>>>  wrote:
>>> 
>>> Overloads are ugly. The very existence of an overloaded function usually 
>>> means a lack of available abstractions, or an insufficient abstraction 
>>> power in the language: exhibit A is conditional conformances to protocols.
>>> 
>>> Overloads are particularly ugly if the overloaded function's input 
>>> represents basically the same thing: a KeyPath is really 
>>> (semantically) just a couple of functions, that is, (A) -> B and (inout 
>>> A,B) -> (), so the (A) -> B is already there, and I like the idea of an 
>>> "extraction" operator that was proposed in the thread. It would be really 
>>> interesting to just use the KeyPath itself wherever a (A) -> B is 
>>> required, but this looks like a hack given the current state of Swift's 
>>> type system.
>>> 
>>> But I like the fundamental idea behind the proposal: KeyPaths give Swift a 
>>> boost in expressive power, and there's probably plenty of use cases that 
>>> will emerge in the future.
>>> 
>>> Thanks
>>> 
>>> 
>>> Elviro
>>> 
 Il giorno 05 lug 2017, alle ore 19:08, Benjamin Herzog via swift-evolution 
  ha scritto:
 
 Hey guys,
 
 I would like to pitch a small convenient change to the Swift stdlib. With 
 KeyPaths added in SE-0161 I would like to add some convenience calls to 
 map, flatMap and filter in Sequences. To extract properties of an array of 
 objects we currently use trailing closure syntax together with the 
 shorthand $0 for the first closure argument. This is still kind of verbose 
 and also hard to read in some situations.
 I think it is much better to understand what is going on when using the 
 type safe KeyPaths for that. I already implemented a working solution and 
 would like to pitch the idea here to get some feedback before opening the 
 swift evolution proposal.
 I propose using 
 
 persons.flatMap(keyPath: \.name)
 
 over
 
 persons.flatMap { $0.name }
 
 Link to pull request: https://github.com/apple/swift/pull/10760
 
 Link to proposal draft: 
 https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
 
 Thanks in advance for your feedback!
 __
 
 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
> 
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread BJ Homer via swift-evolution
‘$' as a prefix is reserved for use by the debugger, so it cannot be used as a 
conversion operator here.

-BJ

> On Jul 11, 2017, at 10:12 AM, Robert Bennett via swift-evolution 
>  wrote:
> 
> It seems that there is some consensus that the proper way to achieve this is 
> not to overload map et al. but to provide a way to convert KeyPath to a 
> function. I agree – not only is this “cheaper”, as overloads of these 
> functions will not need to be written, but it’s also more general and may 
> prove useful in a context that we currently don’t foresee.
> 
> This being the case, I’ll repeat my proposal that the optimal way to achieve 
> this is to make $ the conversion “operator” (although it need not, and 
> probably should not, be a full-fledged operator), so that $keyPath –> { 
> $0[keyPath: keyPath] }
> 
> On Jul 11, 2017, at 11:13 AM, Elviro Rocca via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> 
>> Overloads are ugly. The very existence of an overloaded function usually 
>> means a lack of available abstractions, or an insufficient abstraction power 
>> in the language: exhibit A is conditional conformances to protocols.
>> 
>> Overloads are particularly ugly if the overloaded function's input 
>> represents basically the same thing: a KeyPath is really (semantically) 
>> just a couple of functions, that is, (A) -> B and (inout A,B) -> (), so the 
>> (A) -> B is already there, and I like the idea of an "extraction" operator 
>> that was proposed in the thread. It would be really interesting to just use 
>> the KeyPath itself wherever a (A) -> B is required, but this looks like 
>> a hack given the current state of Swift's type system.
>> 
>> But I like the fundamental idea behind the proposal: KeyPaths give Swift a 
>> boost in expressive power, and there's probably plenty of use cases that 
>> will emerge in the future.
>> 
>> Thanks
>> 
>> 
>> Elviro
>> 
>>> Il giorno 05 lug 2017, alle ore 19:08, Benjamin Herzog via swift-evolution 
>>> mailto:swift-evolution@swift.org>> ha scritto:
>>> 
>>> Hey guys,
>>> 
>>> I would like to pitch a small convenient change to the Swift stdlib. With 
>>> KeyPaths added in SE-0161 I would like to add some convenience calls to 
>>> map, flatMap and filter in Sequences. To extract properties of an array of 
>>> objects we currently use trailing closure syntax together with the 
>>> shorthand $0 for the first closure argument. This is still kind of verbose 
>>> and also hard to read in some situations.
>>> I think it is much better to understand what is going on when using the 
>>> type safe KeyPaths for that. I already implemented a working solution and 
>>> would like to pitch the idea here to get some feedback before opening the 
>>> swift evolution proposal.
>>> I propose using 
>>> 
>>> persons.flatMap(keyPath: \.name)
>>> 
>>> over
>>> 
>>> persons.flatMap { $0.name }
>>> 
>>> Link to pull request: https://github.com/apple/swift/pull/10760 
>>> 
>>> 
>>> Link to proposal draft: 
>>> https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
>>>  
>>> 
>>> 
>>> Thanks in advance for your feedback!
>>> __
>>> 
>>> 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

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Robert Bennett via swift-evolution
It seems that there is some consensus that the proper way to achieve this is 
not to overload map et al. but to provide a way to convert KeyPath to a 
function. I agree – not only is this “cheaper”, as overloads of these functions 
will not need to be written, but it’s also more general and may prove useful in 
a context that we currently don’t foresee.

This being the case, I’ll repeat my proposal that the optimal way to achieve 
this is to make $ the conversion “operator” (although it need not, and probably 
should not, be a full-fledged operator), so that $keyPath –> { $0[keyPath: 
keyPath] }

> On Jul 11, 2017, at 11:13 AM, Elviro Rocca via swift-evolution 
>  wrote:
> 
> Overloads are ugly. The very existence of an overloaded function usually 
> means a lack of available abstractions, or an insufficient abstraction power 
> in the language: exhibit A is conditional conformances to protocols.
> 
> Overloads are particularly ugly if the overloaded function's input represents 
> basically the same thing: a KeyPath is really (semantically) just a 
> couple of functions, that is, (A) -> B and (inout A,B) -> (), so the (A) -> B 
> is already there, and I like the idea of an "extraction" operator that was 
> proposed in the thread. It would be really interesting to just use the 
> KeyPath itself wherever a (A) -> B is required, but this looks like a 
> hack given the current state of Swift's type system.
> 
> But I like the fundamental idea behind the proposal: KeyPaths give Swift a 
> boost in expressive power, and there's probably plenty of use cases that will 
> emerge in the future.
> 
> Thanks
> 
> 
> Elviro
> 
>> Il giorno 05 lug 2017, alle ore 19:08, Benjamin Herzog via swift-evolution 
>>  ha scritto:
>> 
>> Hey guys,
>> 
>> I would like to pitch a small convenient change to the Swift stdlib. With 
>> KeyPaths added in SE-0161 I would like to add some convenience calls to map, 
>> flatMap and filter in Sequences. To extract properties of an array of 
>> objects we currently use trailing closure syntax together with the shorthand 
>> $0 for the first closure argument. This is still kind of verbose and also 
>> hard to read in some situations.
>> I think it is much better to understand what is going on when using the type 
>> safe KeyPaths for that. I already implemented a working solution and would 
>> like to pitch the idea here to get some feedback before opening the swift 
>> evolution proposal.
>> I propose using 
>> 
>> persons.flatMap(keyPath: \.name)
>> 
>> over
>> 
>> persons.flatMap { $0.name }
>> 
>> Link to pull request: https://github.com/apple/swift/pull/10760
>> 
>> Link to proposal draft: 
>> https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
>> 
>> Thanks in advance for your feedback!
>> __
>> 
>> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-11 Thread Elviro Rocca via swift-evolution
Overloads are ugly. The very existence of an overloaded function usually means 
a lack of available abstractions, or an insufficient abstraction power in the 
language: exhibit A is conditional conformances to protocols.

Overloads are particularly ugly if the overloaded function's input represents 
basically the same thing: a KeyPath is really (semantically) just a couple 
of functions, that is, (A) -> B and (inout A,B) -> (), so the (A) -> B is 
already there, and I like the idea of an "extraction" operator that was 
proposed in the thread. It would be really interesting to just use the 
KeyPath itself wherever a (A) -> B is required, but this looks like a hack 
given the current state of Swift's type system.

But I like the fundamental idea behind the proposal: KeyPaths give Swift a 
boost in expressive power, and there's probably plenty of use cases that will 
emerge in the future.

Thanks


Elviro

> Il giorno 05 lug 2017, alle ore 19:08, Benjamin Herzog via swift-evolution 
>  ha scritto:
> 
> Hey guys,
> 
> I would like to pitch a small convenient change to the Swift stdlib. With 
> KeyPaths added in SE-0161 I would like to add some convenience calls to map, 
> flatMap and filter in Sequences. To extract properties of an array of objects 
> we currently use trailing closure syntax together with the shorthand $0 for 
> the first closure argument. This is still kind of verbose and also hard to 
> read in some situations.
> I think it is much better to understand what is going on when using the type 
> safe KeyPaths for that. I already implemented a working solution and would 
> like to pitch the idea here to get some feedback before opening the swift 
> evolution proposal.
> I propose using 
> 
> persons.flatMap(keyPath: \.name)
> 
> over
> 
> persons.flatMap { $0.name }
> 
> Link to pull request: https://github.com/apple/swift/pull/10760 
> 
> 
> Link to proposal draft: 
> https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
>  
> 
> 
> Thanks in advance for your feedback!
> __
> 
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-10 Thread Dave Abrahams via swift-evolution

on Sun Jul 09 2017, Brent Royal-Gordon  wrote:

> But however we achieve it, I think a spoonful of
> syntactic sugar would help the medicine go down.

Let me be clear: syntactic sugar matters.  Otherwise, we'd all be
programming directly in LLVM IR.  It's just a question of what you have
to pay to get it.

>> By the way, if you're worried about whether subtyping will fly, I've
>> recently been thinking there might be a role for a “promotion” operator
>> that enables lossless “almost-implicit” conversions, e.g.:
>> 
>>someNumber^  is equivalent tonumericCast(someNumber)
>>\.someKeyPath^   is equivalent to{ $0\.someKeyPath }
>>someSubstring^   is equivalent toString(someSubstring)
>> 
>>etc.
>
> I actually played with something like this years ago (pre-open source,
> IIRC), but I used `^` as a prefix operator and made it support only
> widening conversions. But it was old code, and redoing it nerd-sniped
> me so hard that I kind of ended up making a whole GitHub project from
> it: 
>
> The main component is an `Upconvertible` protocol which encapsulates
> the conversion. That works really well in some ways, but it also
> creates some important limitations:
>
> 1. I had trouble incorporating downconversions in a reasonable
> way. Key paths in particular would require either compiler support or
> some really hacky, fragile code that opened up the closure context and
> pulled out the KeyPath object.
>
> 2. There's no good way to support more than one upconversion from a
> single type. (For instance, you can't make `UInt16` upconvert to both
> `Uint32` and `Int32`.)
>
> 3. Even if #2 were somehow fixed, you still can't make all
> `LosslessStringConvertible` types conform to `Upconvertible`.
>
> 4. Can't upconvert from a structural type, of course.

AFAICT, all of the above come down to having tried to build this idiom
around a protocol with an associated type.  I think of it as a
special-case syntactic shortcut for “value-preserving conversion to
deduced type.” Just overload the operator and be done with it.  Maybe
protocols like BinaryInteger should have a generic operator, but this
doesn't deserve a protocol of its own.

> 5. I wanted to support passing through any number of valid
> upconversions with a single `^` operator, but the only way I could
> find to do that was to overload the operator with a two-step version,
> a three-step version, etc.
>
> 6. Upconverting a `\.keyPath` expression caused an ambiguity error; I
> had to overload the operator to make it favor `KeyPath`. (Workaround
> code:
> https://github.com/brentdax/Upconvert/blob/master/Upconvert/Conformances/KeyPath.swift#L25)

Meh; that's a fact of life when overloading.

> Several-to-all of these could be avoided with a built-in language feature.

IMO no language feature is needed for this.

> As for the ergonomics…well, `people.map(^\.name)` definitely feels
> better than the closure alternative. But it's something you have to
> learn is possible, and even if you knew about `^` in the context of
> (say) numeric conversions, I'm not sure people would think to try it
> there. It basically means you need to know about three slightly
> esoteric features instead of two; I'm not sure people will discover
> that.

Yes, but there's a trade-off between discoverability, and introducing
more implicit conversions, which will slow down the type checker and can
make errors harder to understand.  Note: I'm not arguing for either
approach in particular.  They're just available alternatives.

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-09 Thread Brent Royal-Gordon via swift-evolution
> On Jul 7, 2017, at 4:27 PM, Dave Abrahams  wrote:
> 
> Yes, that's a lot of extra syntax.  But again, you've used an
> abbreviated, single identifier for the property and a
> short, non-descriptive identifier for the array.  Let's make this a
> fair/realistic comparison:
> 
>gradeHistories.map { $0[keyPath: \.average] }
> 
> vs.
>gradeHistories.map(\.average)

FWIW, this example isn't terribly realistic because you'd normally use a direct 
reference to a property, rather than a key path, in the transform closure. But 
after correcting for that, I don't think it changes the calculus very much:

gradeHistories.map { $0[keyPath: aggregate] }
gradeHistories.map(aggregate)

>> That's essentially what happened with the much-requested placeholder
>> syntax:
> 
> Sorry, I may have missed that discussion.

It was somewhere back in the Swift 3 timeframe. Short version is, people wanted 
a way to say something like:

numbers.map(abs(_) + 1)

But it was pointed out that this is fundamentally unclear about which of these 
you mean:

numbers.map({ abs($0) } + 1)
numbers.map({ abs($0) + 1 })
{ numbers.map(abs($0) + 1) }

And so the proponents pretty much dropped the suggestion.

> It's not fundamentally weird.  I'm just not sure it's important.

I'm not going to argue key-paths-as-functions are critical, because they're 
not. But I think they should be on the to-do list. And I think that niceties 
*like* this are, as a whole, something we should try to deliver more of. The 
soundness work that's being prioritized right now is really important, so I'm 
not sure how exactly to manage this—maybe these niceties should be left more to 
the community while full-time professional contributors focus more on core 
design issues and deep refactoring. But however we achieve it, I think a 
spoonful of syntactic sugar would help the medicine go down.

> If it
> actually is important, as I said, I feel very strongly that it shouldn't
> require anyone to create an overload of map, because that quickly leads
> to overloading everything that takes a closure.

I agree that we shouldn't overload `map` to specially support keypaths, except 
perhaps as a stopgap measure while we support subtyping more fully. And I don't 
think this is important enough to justify a stopgap.

>> There's one more reason I think we should do this. It is not about the
>> technology; it is not even really about the ergonomics. It's more
>> about language "marketing", for lack of a better term.
>> 
>> I think we were all surprised by the SE-0110 backlash. But in
>> hindsight, I think it's pretty easy to explain. During the Swift 3 and
>> 4 cycles, we systematically stripped conveniences and sugar from
>> higher-order functions. We have good reasons to do this; we want to
>> pare things down, fix the foundations, and then build up new
>> conveniences. Eat your vegetables now and you can have dessert later.
>> 
>> But we're entering our second year of eating nothing but
>> vegetables. It's very difficult to maintain a strict diet forever,
>> especially when—like the vast majority of Swift's users who don't
>> participate in evolution or implementation—you don't really see the
>> point of it. It's hard to blame them for being tired of it, or for
>> complaining when yet another tasty food is pulled off the menu.
>> 
>> Offering a treat like this on occasion will help ease the pain of
>> losing the things we *need* to take away. And this is a particularly
>> good candidate because, although it's a convenience for higher-order
>> functions—which is where the pain is felt—it has little to do with
>> parameter handling, the area where we actually need to remove things
>> and refactor. It's like a dessert of ultra-dark chocolate—it's a treat
>> that doesn't set the actual goal back very far.
>> 
>> In the abstract, "fundamentals now, sugar later" is the right
>> approach. But it can't be considered "right" if the users won't accept
>> it. So let's look for opportunities to add conveniences where we
>> can. Maybe this isn't the right feature—subtyping is always a bit
>> perilous—but we should be on the lookout for features like this one,
>> places where we can improve things for our functional programming fans
>> without obstructing our own efforts to clean up parameter handling.
> 
> These are all good arguments.  For me it's a question of priorities and
> long-term, irrevocable impacts.
> 
> By the way, if you're worried about whether subtyping will fly, I've
> recently been thinking there might be a role for a “promotion” operator
> that enables lossless “almost-implicit” conversions, e.g.:
> 
>someNumber^  is equivalent tonumericCast(someNumber)
>\.someKeyPath^   is equivalent to{ $0\.someKeyPath }
>someSubstring^   is equivalent toString(someSubstring)
> 
>etc.

I actually played with something like this years ago (pre-open source, IIRC), 
but I used `^` as a prefix operato

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-09 Thread Benjamin Herzog via swift-evolution
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)

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)

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 {
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



> On 9. Jul 2017, at 13:09, Karl Wagner  wrote:
> 
> I agree that it should be completely implicit.
> 
> KeyPaths are simply chains of partially-applied properties and subscripts. At 
> the same time, it was noted in the KeyPath proposal that a similar mechanism 
> might be used to model chains of partially-applied functions. I think that 
> having both types be convertible to a closure would be sensible.
> 
> In fact, you could argue for a general-purpose “Executable” protocol which 
> would allow any conforming object to be implicitly used as a 
> function/closure. Command-style objects such as predictes and transformers 
> would also benefit from such a feature.
> 
> - Karl
> 



signature.asc
Description: Message signed with OpenPGP
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-09 Thread Karl Wagner via swift-evolution
  
  

 I agree that it should be completely implicit.
  

  
KeyPaths are simply chains of partially-applied properties and subscripts. At 
the same time, it was noted in the KeyPath proposal that a similar mechanism 
might be used to model chains of partially-applied functions. I think that 
having both types be convertible to a closure would be sensible.
  

  
In fact, you could argue for a general-purpose “Executable” protocol which 
would allow any conforming object to be implicitly used as a function/closure. 
Command-style objects such as predictes and transformers would also benefit 
from such a feature.
  

  
- Karl
  

  

  

  

  
  

  
  
>   
> On Jul 8, 2017 at 11:56 pm,   (mailto:swift-evolution@swift.org)>  wrote:
>   
>   
> 
> Is this operator common in other languages? I would actually expect that the 
> conversation is not 'almost-implicit' but completely implicit instead. I 
> think both - a prefix and postfix operator - are not obvious enough what 
> happens here,   especially because this kind of conversion is not happening 
> in other parts of the language.  
> All conversions are implicit (from explicit type to protocol, from Swift 
> stdlib types to Objective-C types, from any type to Any, …) currently.
>   
>
>   
>   
> __
>   
>
>   
> Benjamin Herzog
>   
>   
>   
> 
>   
> >   
> > On 8. Jul 2017, at 22:10, Hooman Mehr via swift-evolution  
> > mailto:swift-evolution@swift.org)>  wrote:
> >   
> >   
> > 
> > I like this promote operator idea. I have been defining similar operators 
> > for specific projects almost at random. It makes sense to come up with a 
> > well-defined behavior and name for such operators, as a common practice as 
> > you suggest.  
> >
> >   
> > The problem with the postfix operator is that it does not currently work 
> > without an extra set of parenthesis:
> >   
> >
> >   
> >   
> > postfix  operator  ^
> >   
> >
> >   
> > postfix   func  ^(lhs:  KeyPath) ->  (T)->U  {  return  { 
> > $0[keyPath: lhs] } }
> >   
> >
> >   
> > struct  Guy {  let  name:  String  }
> >   
> >
> >   
> > let  guys = [
> >   
> > Guy(name:  "Benjamin"),
> >   
> > Guy(name:  "Dave"),
> >   
> > Guy(name:  "Brent"),
> >   
> > Guy(name:  "Max")
> >   
> > ]
> >   
> >
> >   
> > guys.map(\.name^)  // Error: Invalid component of Swift key path
> >   
> >
> >   
> > guys.map((\.name)^)  // This works
> >   
> >   
> >
> >   
> > Is this a bug?   
> >   
> >
> >   
> > That is the reason I used a prefix operator (~) in my suggestion in the a 
> > previous e-mail on this thread.
> >   
> >   
> >   
>   
>   
>  ___ 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-08 Thread Benjamin Herzog via swift-evolution
The $ 'operator' would also be a good option because it matches the $0 syntax 
which is not far away logically.
__

Benjamin Herzog



> On 9. Jul 2017, at 00:00, Robert Bennett via swift-evolution 
>  wrote:
> 
> I’m not sure about needing a universal promotion operator in Swift. However, 
> in the case of KeyPaths, I think a leading $ (not currently a valid operator) 
> would work well.
> 
> prefix operator $
> prefix func $(rhs: KeyPath) -> (T)->U { return { $0[keyPath: rhs] } 
> }
> 
> guys.map($\.name)
> 
> This reads really well to me because the $ is suggestive of the 
> “functionization” of the KeyPath. Also, this is guaranteed to have no 
> compatibility issues (right?) because it’s currently forbidden.
> 
> (I’m only suggesting giving the leading $ this functionality, not necessarily 
> achieving this by making it a valid operator — in fact, it would probably be 
> best if this functionality were “hard-coded” just as it is currently 
> hard-coded for use in $0.)
> 
>> On Jul 8, 2017, at 5:46 PM, Benjamin Herzog via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> Is this operator common in other languages? I would actually expect that the 
>> conversation is not 'almost-implicit' but completely implicit instead. I 
>> think both - a prefix and postfix operator - are not obvious enough what 
>> happens here, especially because this kind of conversion is not happening in 
>> other parts of the language.
>> All conversions are implicit (from explicit type to protocol, from Swift 
>> stdlib types to Objective-C types, from any type to Any, …) currently.
>> 
>> __
>> 
>> Benjamin Herzog
>> 
>>> On 8. Jul 2017, at 22:10, Hooman Mehr via swift-evolution 
>>> mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> I like this promote operator idea. I have been defining similar operators 
>>> for specific projects almost at random. It makes sense to come up with a 
>>> well-defined behavior and name for such operators, as a common practice as 
>>> you suggest.
>>> 
>>> The problem with the postfix operator is that it does not currently work 
>>> without an extra set of parenthesis:
>>> 
>>> postfix operator ^
>>> 
>>> postfix func ^(lhs: KeyPath) -> (T)->U { return { $0[keyPath: 
>>> lhs] } }
>>> 
>>> struct Guy { let name: String }
>>> 
>>> let guys = [
>>> Guy(name: "Benjamin"),
>>> Guy(name: "Dave"),
>>> Guy(name: "Brent"),
>>> Guy(name: "Max")
>>> ]
>>> 
>>> guys.map(\.name^) // Error: Invalid component of Swift key path
>>> 
>>> guys.map((\.name)^) // This works
>>> 
>>> Is this a bug?
>>> 
>>> That is the reason I used a prefix operator (~) in my suggestion in the a 
>>> previous e-mail on this thread.
>> 
>> ___
>> 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



signature.asc
Description: Message signed with OpenPGP
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-08 Thread Robert Bennett via swift-evolution
I’m not sure about needing a universal promotion operator in Swift. However, in 
the case of KeyPaths, I think a leading $ (not currently a valid operator) 
would work well.

prefix operator $
prefix func $(rhs: KeyPath) -> (T)->U { return { $0[keyPath: rhs] } }

guys.map($\.name)

This reads really well to me because the $ is suggestive of the 
“functionization” of the KeyPath. Also, this is guaranteed to have no 
compatibility issues (right?) because it’s currently forbidden.

(I’m only suggesting giving the leading $ this functionality, not necessarily 
achieving this by making it a valid operator — in fact, it would probably be 
best if this functionality were “hard-coded” just as it is currently hard-coded 
for use in $0.)

> On Jul 8, 2017, at 5:46 PM, Benjamin Herzog via swift-evolution 
>  wrote:
> 
> Is this operator common in other languages? I would actually expect that the 
> conversation is not 'almost-implicit' but completely implicit instead. I 
> think both - a prefix and postfix operator - are not obvious enough what 
> happens here, especially because this kind of conversion is not happening in 
> other parts of the language.
> All conversions are implicit (from explicit type to protocol, from Swift 
> stdlib types to Objective-C types, from any type to Any, …) currently.
> 
> __
> 
> Benjamin Herzog
> 
>> On 8. Jul 2017, at 22:10, Hooman Mehr via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> I like this promote operator idea. I have been defining similar operators 
>> for specific projects almost at random. It makes sense to come up with a 
>> well-defined behavior and name for such operators, as a common practice as 
>> you suggest.
>> 
>> The problem with the postfix operator is that it does not currently work 
>> without an extra set of parenthesis:
>> 
>> postfix operator ^
>> 
>> postfix func ^(lhs: KeyPath) -> (T)->U { return { $0[keyPath: lhs] 
>> } }
>> 
>> struct Guy { let name: String }
>> 
>> let guys = [
>> Guy(name: "Benjamin"),
>> Guy(name: "Dave"),
>> Guy(name: "Brent"),
>> Guy(name: "Max")
>> ]
>> 
>> guys.map(\.name^) // Error: Invalid component of Swift key path
>> 
>> guys.map((\.name)^) // This works
>> 
>> Is this a bug? 
>> 
>> That is the reason I used a prefix operator (~) in my suggestion in the a 
>> previous e-mail on this thread.
> 
> ___
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-08 Thread Benjamin Herzog via swift-evolution
Is this operator common in other languages? I would actually expect that the 
conversation is not 'almost-implicit' but completely implicit instead. I think 
both - a prefix and postfix operator - are not obvious enough what happens 
here, especially because this kind of conversion is not happening in other 
parts of the language.
All conversions are implicit (from explicit type to protocol, from Swift stdlib 
types to Objective-C types, from any type to Any, …) currently.

__

Benjamin Herzog

> On 8. Jul 2017, at 22:10, Hooman Mehr via swift-evolution 
>  wrote:
> 
> I like this promote operator idea. I have been defining similar operators for 
> specific projects almost at random. It makes sense to come up with a 
> well-defined behavior and name for such operators, as a common practice as 
> you suggest.
> 
> The problem with the postfix operator is that it does not currently work 
> without an extra set of parenthesis:
> 
> postfix operator ^
> 
> postfix func ^(lhs: KeyPath) -> (T)->U { return { $0[keyPath: lhs] 
> } }
> 
> struct Guy { let name: String }
> 
> let guys = [
> Guy(name: "Benjamin"),
> Guy(name: "Dave"),
> Guy(name: "Brent"),
> Guy(name: "Max")
> ]
> 
> guys.map(\.name^) // Error: Invalid component of Swift key path
> 
> guys.map((\.name)^) // This works
> 
> Is this a bug?
> 
> That is the reason I used a prefix operator (~) in my suggestion in the a 
> previous e-mail on this thread.



signature.asc
Description: Message signed with OpenPGP
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-08 Thread Hooman Mehr via swift-evolution
I like this promote operator idea. I have been defining similar operators for 
specific projects almost at random. It makes sense to come up with a 
well-defined behavior and name for such operators, as a common practice as you 
suggest.

The problem with the postfix operator is that it does not currently work 
without an extra set of parenthesis:

postfix operator ^

postfix func ^(lhs: KeyPath) -> (T)->U { return { $0[keyPath: lhs] } }

struct Guy { let name: String }

let guys = [
Guy(name: "Benjamin"),
Guy(name: "Dave"),
Guy(name: "Brent"),
Guy(name: "Max")
]

guys.map(\.name^) // Error: Invalid component of Swift key path

guys.map((\.name)^) // This works

Is this a bug? 

That is the reason I used a prefix operator (~) in my suggestion in the a 
previous e-mail on this thread.


> On Jul 7, 2017, at 4:27 PM, Dave Abrahams via swift-evolution 
>  wrote:
> 
> By the way, if you're worried about whether subtyping will fly, I've
> recently been thinking there might be a role for a “promotion” operator
> that enables lossless “almost-implicit” conversions, e.g.:
> 
>someNumber^  is equivalent tonumericCast(someNumber)
>\.someKeyPath^   is equivalent to{ $0\.someKeyPath }
>someSubstring^   is equivalent toString(someSubstring)
> 
>etc.
> 
> This convenience can be implemented by anyone today for keypaths, and
> will provide nearly the syntax you're looking for.  This is exactly the
> sort of thing I'd love to see become a widespread existing practice
> before we incorporate it in the standard library, so we could properly
> judge its impact on real code.

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-07 Thread Dave Abrahams via swift-evolution

on Thu Jul 06 2017, Brent Royal-Gordon  wrote:

>> On Jul 6, 2017, at 9:13 AM, Dave Abrahams  wrote:
>> 
>> I'm not sure what you're objecting to about this.  Is it the very
>> appearance of curly braces?
>
> I went to bed thinking that maybe I should have explained that better,
> and I guess I was right. ;^) Here's why I think we should do something
> here.
>
> From what I can tell, mapping or filtering on a single property is
> exceptionally common. I ran a few regexes on the Swift code present on
> my machine (basically the stuff in the Swift repos, plus my
> open-source projects, plus my closed-source stuff, plus various
> playgrounds and things) to see how common different kinds of `map`,
> `filter`, and `flatMap` closures were:
>
>   2142OP { …$0… }
>   1835OP(function) or OP(some.method)
>   589 OP { $0.property } or OP { $0.some.property }
>   564 OP { $0.property }
>   525 OP { function(…$0…) } or OP { some.method(…$0…) }
>   186 OP { $0.method(…) }
>   153 OP { function($0) } or OP { some.method($0) }
>   100 OP { $0 as SomeType } or OP { $0 as? SomeType } or OP { 
> $0 as! SomeType }
>   52  OP { $0.method() }
>   35  OP { collection[…$0…] } or OP { some.collection[…$0…] }
>   20  OP { collection[$0] } or OP { some.collection[$0] }
>   13  OP { $0! }
>
> (Simple regex-based match of `map`, `flatMap`, and `filter`
> calls. Permits various spacing schemes and `try`. If you want to run
> it on a more representative sample, the script is here, requires
> fish(1):
> https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f)
>
> So, at least in my unscientific sample, more than a quarter of
> map/filter/flatMap calls which use `$0` simply look up a property or
> chain of properties on it. 

Totally believable.

> If we want to make something about `map`
> and friends more convenient, this seems like a good place to look.
>
> (Using a straight function is a few times more common than a property,
> but given that this is also the syntax used to abstract over a closure
> body, I'm not sure how much weight to put on that fact.)
>
> So what's wrong with what we have now? Syntactic weight. Consider this 
> expression:
>
>   person.map { $0.name }
>
> The "loudest" parts of this expression are the closure brackets and
> the `$0`, but they are actually the *least* important. 

That's funny, my eye skips right over them and focuses on “.name” Not
kidding at all.  And “name” is a single fairly short, un-chained
identifier.

> They do not express anything about what this line of code *does*; they
> exist solely to tell the compiler how to do it. They are pure glue
> code, and serve only to obscure the actual intent. Compare that to:
>
>   person.map(\.name)
>
> Here, we still have a glue character (the `\`), but it's just one, and
> it's relatively inconspicuous compared to something like `$0`.

Meh.  This in particular doesn't look like a major improvement.

> That's not *too* bad, though. It gets a lot worse when the key path is
> actually in a variable:
>
>   array.map { $0[keyPath: prop] }

> Again, look at how much of this line is given over to adapting a line
> of code to the compiler—and how little that actually matters when
> understanding what the line does. The most important thing in that
> expression is `prop`, but it's completely lost in this sea of
> irrelevant syntax. Compare to:
>
>   array.map(prop)

Yes, that's a lot of extra syntax.  But again, you've used an
abbreviated, single identifier for the property and a
short, non-descriptive identifier for the array.  Let's make this a
fair/realistic comparison:

gradeHistories.map { $0[keyPath: \.average] }

vs.
gradeHistories.map(\.average)


Yep, I agree that passing a keypath directly is still much nicer in this
case.

> Which puts that piece of information front and center.
>
> If there was an argument that the case was too complex to handle
> nicely, I think we could justify leaving it alone. 

It's not the “too complex” criterion I'd want to talk about—it's the
“how often do you need it” criterion.  If it's rare, it doesn't matter
so much (I don't have an opinion about whether it is in fact rare).

> That's essentially what happened with the much-requested placeholder
> syntax:

Sorry, I may have missed that discussion.

> Lots of people wanted it, but critics pointed out the fundamental
> ambiguity of the syntax, and after spending gallons of electrons
> arguing about it, the proponents pretty much backed off. But key paths
> don't have that problem—they always work the same way and are
> completely unambiguous, so there's no scope for misinterpretation. And
> the cases key paths can handle appear to be about as common as the
> cases placeholder syntax was intended for.
>
>
> Nor is there a strong argument that the suggested beha

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-07 Thread Hooman Mehr via swift-evolution
Considering these observations and more from Brent and Dave, I support 
ultimately making KeyPath) a subtype of (T)->U as suggested.

If someone really wants this now, I would go with a prefix operator as a 
stopgap:

prefix operator ~

prefix func ~(lhs: KeyPath) -> (T)->U { return { $0[keyPath: lhs] } }

struct Guy { let name: String }

let guys = [
Guy(name: "Benjamin"),
Guy(name: "Dave"),
Guy(name: "Brent"),
Guy(name: "Max")
]

guys.map(~\.name)


> On Jul 7, 2017, at 11:15 AM, Max Moiseev via swift-evolution 
>  wrote:
> 
> A few more observations not necessarily supporting either side:
> 
> - Why only map, flatMap, and filter? drop, prefix, and sorted can also 
> benefit from keyPaths.
> - Adding these overloads only to Sequence will have a very interesting side 
> effect on code like: xs.lazy.map(\.name), because lazy. Therefore all these 
> overloads will have to be duplicated everywhere.
> - Naive implementation (the one that simply calls the closure taking 
> overload) was 4 times slower when I quickly tested it. On the positive side 
> though, this same naive implementation does not have to be a part of standard 
> library and can easily be provided by a third-party package...
> 
> Max
>> On Jul 5, 2017, at 10:08 AM, Benjamin Herzog via swift-evolution 
>> mailto:swift-evolution@swift.org>> wrote:
>> 
>> Hey guys,
>> 
>> I would like to pitch a small convenient change to the Swift stdlib. With 
>> KeyPaths added in SE-0161 I would like to add some convenience calls to map, 
>> flatMap and filter in Sequences. To extract properties of an array of 
>> objects we currently use trailing closure syntax together with the shorthand 
>> $0 for the first closure argument. This is still kind of verbose and also 
>> hard to read in some situations.
>> I think it is much better to understand what is going on when using the type 
>> safe KeyPaths for that. I already implemented a working solution and would 
>> like to pitch the idea here to get some feedback before opening the swift 
>> evolution proposal.
>> I propose using 
>> 
>> persons.flatMap(keyPath: \.name)
>> 
>> over
>> 
>> persons.flatMap { $0.name }
>> 
>> Link to pull request: https://github.com/apple/swift/pull/10760 
>> 
>> 
>> Link to proposal draft: 
>> https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
>>  
>> 
>> 
>> Thanks in advance for your feedback!
>> __
>> 
>> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-07 Thread Max Moiseev via swift-evolution
A few more observations not necessarily supporting either side:

- Why only map, flatMap, and filter? drop, prefix, and sorted can also benefit 
from keyPaths.
- Adding these overloads only to Sequence will have a very interesting side 
effect on code like: xs.lazy.map(\.name), because lazy. Therefore all these 
overloads will have to be duplicated everywhere.
- Naive implementation (the one that simply calls the closure taking overload) 
was 4 times slower when I quickly tested it. On the positive side though, this 
same naive implementation does not have to be a part of standard library and 
can easily be provided by a third-party package...

Max
> On Jul 5, 2017, at 10:08 AM, Benjamin Herzog via swift-evolution 
>  wrote:
> 
> Hey guys,
> 
> I would like to pitch a small convenient change to the Swift stdlib. With 
> KeyPaths added in SE-0161 I would like to add some convenience calls to map, 
> flatMap and filter in Sequences. To extract properties of an array of objects 
> we currently use trailing closure syntax together with the shorthand $0 for 
> the first closure argument. This is still kind of verbose and also hard to 
> read in some situations.
> I think it is much better to understand what is going on when using the type 
> safe KeyPaths for that. I already implemented a working solution and would 
> like to pitch the idea here to get some feedback before opening the swift 
> evolution proposal.
> I propose using 
> 
> persons.flatMap(keyPath: \.name)
> 
> over
> 
> persons.flatMap { $0.name }
> 
> Link to pull request: https://github.com/apple/swift/pull/10760 
> 
> 
> Link to proposal draft: 
> https://github.com/BenchR267/swift-evolution/blob/keypath-based-map/proposals/0181-keypath-based-map-flatmap-filter.md
>  
> 
> 
> Thanks in advance for your feedback!
> __
> 
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-06 Thread Robert Bennett via swift-evolution
I agree with Brent (and others) that this is sugar worth having. If keypaths 
had been introduced earlier in the language’s evolution, array.map { 
$0.property } would look as ugly, and as Brent put it, as noisy, as array.map { 
f($0) }; we only accept it because we’ve had no better alternative. It feels as 
clunky to use a function to extract a property from a mapped element as it does 
to construct a closure whose only purpose is to call another function. If 
array.map(f) is allowed, then array.map(\.property) should be as well. It’s 
only natural.

That said, this addition does not necessarily need to occur soon. Xiaodi is 
right that once this is added, we’re pretty much stuck with it. Perhaps only 
after an evaluation of how keypaths are used by the Swift community should this 
addition to the language be reconsidered. There’s no need to permanently shelve 
the idea now, though. You get to eat dessert once you’ve eaten all your 
vegetables.

> On Jul 6, 2017, at 11:01 PM, Xiaodi Wu via swift-evolution 
>  wrote:
> 
> The lesson I took away from SE-0110 is that conveniences, once offered, are 
> disruptive to take away. People who don't delve deeply into the reasons that 
> make the conveniences unworkable in the grand scheme of things are upset when 
> these features are taken away because specific use cases are made less 
> ergonomic.
> 
> Since, as you say, subtyping is always a bit perilous, if this idea is 
> adopted but turns out to have important overlooked problems, trying to roll 
> it back is going to cause a lot of pain. Not offering it in the first place, 
> however, is by comparison only a slight inconvenience. No one has ever died 
> of not drinking alcohol; plenty have died of alcohol withdrawal. Therefore, 
> my lesson from SE-0110 is: vegetables now, vegetables forever.
> On Thu, Jul 6, 2017 at 21:38 Brent Royal-Gordon via swift-evolution 
>  wrote:
>>> On Jul 6, 2017, at 9:13 AM, Dave Abrahams  wrote:
>>> 
>>> I'm not sure what you're objecting to about this.  Is it the very
>>> appearance of curly braces?
>> 
>> 
>> I went to bed thinking that maybe I should have explained that better, and I 
>> guess I was right. ;^) Here's why I think we should do something here.
>> 
>> From what I can tell, mapping or filtering on a single property is 
>> exceptionally common. I ran a few regexes on the Swift code present on my 
>> machine (basically the stuff in the Swift repos, plus my open-source 
>> projects, plus my closed-source stuff, plus various playgrounds and things) 
>> to see how common different kinds of `map`, `filter`, and `flatMap` closures 
>> were:
>> 
>>  2142OP { …$0… }
>>  1835OP(function) or OP(some.method)
>>  589 OP { $0.property } or OP { $0.some.property }
>>  564 OP { $0.property }
>>  525 OP { function(…$0…) } or OP { some.method(…$0…) }
>>  186 OP { $0.method(…) }
>>  153 OP { function($0) } or OP { some.method($0) }
>>  100 OP { $0 as SomeType } or OP { $0 as? SomeType } or OP { 
>> $0 as! SomeType }
>>  52  OP { $0.method() }
>>  35  OP { collection[…$0…] } or OP { some.collection[…$0…] }
>>  20  OP { collection[$0] } or OP { some.collection[$0] }
>>  13  OP { $0! }
>> 
>> (Simple regex-based match of `map`, `flatMap`, and `filter` calls. Permits 
>> various spacing schemes and `try`. If you want to run it on a more 
>> representative sample, the script is here, requires fish(1): 
>> https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f)
>>  
>> So, at least in my unscientific sample, more than a quarter of 
>> map/filter/flatMap calls which use `$0` simply look up a property or chain 
>> of properties on it. If we want to make something about `map` and friends 
>> more convenient, this seems like a good place to look.
>> 
>> (Using a straight function is a few times more common than a property, but 
>> given that this is also the syntax used to abstract over a closure body, I'm 
>> not sure how much weight to put on that fact.)
>> 
>> So what's wrong with what we have now? Syntactic weight. Consider this 
>> expression:
>> 
>>  person.map { $0.name }
>> 
>> The "loudest" parts of this expression are the closure brackets and the 
>> `$0`, but they are actually the *least* important. They do not express 
>> anything about what this line of code *does*; they exist solely to tell the 
>> compiler how to do it. They are pure glue code, and serve only to obscure 
>> the actual intent. Compare that to:
>> 
>>  person.map(\.name)
>> 
>> Here, we still have a glue character (the `\`), but it's just one, and it's 
>> relatively inconspicuous compared to something like `$0`.
>> 
>> That's not *too* bad, though. It gets a lot worse when the key path is 
>> actually in a variable:
>> 
>>  array.map { $0[keyPath: prop] }
>> 
>> Again, look at how much of this line is given over to a

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-06 Thread Xiaodi Wu via swift-evolution
The lesson I took away from SE-0110 is that conveniences, once offered, are
disruptive to take away. People who don't delve deeply into the reasons
that make the conveniences unworkable in the grand scheme of things are
upset when these features are taken away because specific use cases are
made less ergonomic.

Since, as you say, subtyping is always a bit perilous, if this idea is
adopted but turns out to have important overlooked problems, trying to roll
it back is going to cause a lot of pain. Not offering it in the first
place, however, is by comparison only a slight inconvenience. No one has
ever died of not drinking alcohol; plenty have died of alcohol withdrawal.
Therefore, my lesson from SE-0110 is: vegetables now, vegetables forever.
On Thu, Jul 6, 2017 at 21:38 Brent Royal-Gordon via swift-evolution <
swift-evolution@swift.org> wrote:

> On Jul 6, 2017, at 9:13 AM, Dave Abrahams  wrote:
>
> I'm not sure what you're objecting to about this.  Is it the very
> appearance of curly braces?
>
>
> I went to bed thinking that maybe I should have explained that better, and
> I guess I was right. ;^) Here's why I think we should do something here.
>
> From what I can tell, mapping or filtering on a single property is
> exceptionally common. I ran a few regexes on the Swift code present on my
> machine (basically the stuff in the Swift repos, plus my open-source
> projects, plus my closed-source stuff, plus various playgrounds and things)
> to see how common different kinds of `map`, `filter`, and `flatMap`
> closures were:
>
> 2142 OP { …$0… }
> 1835 OP(function) or OP(some.method)
> 589 OP { $0.property } or OP { $0.some.property }
> 564 OP { $0.property }
> 525 OP { function(…$0…) } or OP { some.method(…$0…) }
> 186 OP { $0.method(…) }
> 153 OP { function($0) } or OP { some.method($0) }
> 100 OP { $0 as SomeType } or OP { $0 as? SomeType } or OP { $0 as!
> SomeType }
> 52 OP { $0.method() }
> 35 OP { collection[…$0…] } or OP { some.collection[…$0…] }
> 20 OP { collection[$0] } or OP { some.collection[$0] }
> 13 OP { $0! }
>
> (Simple regex-based match of `map`, `flatMap`, and `filter` calls. Permits
> various spacing schemes and `try`. If you want to run it on a more
> representative sample, the script is here, requires fish(1):
> https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f)
>
> So, at least in my unscientific sample, more than a quarter of
> map/filter/flatMap calls which use `$0` simply look up a property or chain
> of properties on it. If we want to make something about `map` and friends
> more convenient, this seems like a good place to look.
>
> (Using a straight function is a few times more common than a property, but
> given that this is also the syntax used to abstract over a closure body,
> I'm not sure how much weight to put on that fact.)
>
> So what's wrong with what we have now? Syntactic weight. Consider this
> expression:
>
> person.map { $0.name }
>
> The "loudest" parts of this expression are the closure brackets and the
> `$0`, but they are actually the *least* important. They do not express
> anything about what this line of code *does*; they exist solely to tell the
> compiler how to do it. They are pure glue code, and serve only to obscure
> the actual intent. Compare that to:
>
> person.map(\.name)
>
> Here, we still have a glue character (the `\`), but it's just one, and
> it's relatively inconspicuous compared to something like `$0`.
>
> That's not *too* bad, though. It gets a lot worse when the key path is
> actually in a variable:
>
> array.map { $0[keyPath: prop] }
>
> Again, look at how much of this line is given over to adapting a line of
> code to the compiler—and how little that actually matters when
> understanding what the line does. The most important thing in that
> expression is `prop`, but it's completely lost in this sea of irrelevant
> syntax. Compare to:
>
> array.map(prop)
>
> Which puts that piece of information front and center.
>
> If there was an argument that the case was too complex to handle nicely, I
> think we could justify leaving it alone. That's essentially what happened
> with the much-requested placeholder syntax: Lots of people wanted it, but
> critics pointed out the fundamental ambiguity of the syntax, and after
> spending gallons of electrons arguing about it, the proponents pretty much
> backed off. But key paths don't have that problem—they always work the same
> way and are completely unambiguous, so there's no scope for
> misinterpretation. And the cases key paths can handle appear to be about as
> common as the cases placeholder syntax was intended for.
>
> Nor is there a strong argument that the suggested behavior is
> fundamentally "weird". It's pretty natural to think of a `KeyPath Value>` as being a `(Root) -> Value` function, and the `foo.map(\.bar)`
> syntax reads pretty straightforwardly as long as you know what `map` does.
>
> There's one more reason I think we should do this. It is not about the
> technology; i

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-06 Thread Brent Royal-Gordon via swift-evolution
> On Jul 6, 2017, at 9:13 AM, Dave Abrahams  wrote:
> 
> I'm not sure what you're objecting to about this.  Is it the very
> appearance of curly braces?


I went to bed thinking that maybe I should have explained that better, and I 
guess I was right. ;^) Here's why I think we should do something here.

>From what I can tell, mapping or filtering on a single property is 
>exceptionally common. I ran a few regexes on the Swift code present on my 
>machine (basically the stuff in the Swift repos, plus my open-source projects, 
>plus my closed-source stuff, plus various playgrounds and things) to see how 
>common different kinds of `map`, `filter`, and `flatMap` closures were:

2142OP { …$0… }
1835OP(function) or OP(some.method)
589 OP { $0.property } or OP { $0.some.property }
564 OP { $0.property }
525 OP { function(…$0…) } or OP { some.method(…$0…) }
186 OP { $0.method(…) }
153 OP { function($0) } or OP { some.method($0) }
100 OP { $0 as SomeType } or OP { $0 as? SomeType } or OP { 
$0 as! SomeType }
52  OP { $0.method() }
35  OP { collection[…$0…] } or OP { some.collection[…$0…] }
20  OP { collection[$0] } or OP { some.collection[$0] }
13  OP { $0! }

(Simple regex-based match of `map`, `flatMap`, and `filter` calls. Permits 
various spacing schemes and `try`. If you want to run it on a more 
representative sample, the script is here, requires fish(1): 
https://gist.github.com/brentdax/2a8ee2705c39e9948aafedbd81b1366f)
 
So, at least in my unscientific sample, more than a quarter of 
map/filter/flatMap calls which use `$0` simply look up a property or chain of 
properties on it. If we want to make something about `map` and friends more 
convenient, this seems like a good place to look.

(Using a straight function is a few times more common than a property, but 
given that this is also the syntax used to abstract over a closure body, I'm 
not sure how much weight to put on that fact.)

So what's wrong with what we have now? Syntactic weight. Consider this 
expression:

person.map { $0.name }

The "loudest" parts of this expression are the closure brackets and the `$0`, 
but they are actually the *least* important. They do not express anything about 
what this line of code *does*; they exist solely to tell the compiler how to do 
it. They are pure glue code, and serve only to obscure the actual intent. 
Compare that to:

person.map(\.name)

Here, we still have a glue character (the `\`), but it's just one, and it's 
relatively inconspicuous compared to something like `$0`.

That's not *too* bad, though. It gets a lot worse when the key path is actually 
in a variable:

array.map { $0[keyPath: prop] }

Again, look at how much of this line is given over to adapting a line of code 
to the compiler—and how little that actually matters when understanding what 
the line does. The most important thing in that expression is `prop`, but it's 
completely lost in this sea of irrelevant syntax. Compare to:

array.map(prop)

Which puts that piece of information front and center.

If there was an argument that the case was too complex to handle nicely, I 
think we could justify leaving it alone. That's essentially what happened with 
the much-requested placeholder syntax: Lots of people wanted it, but critics 
pointed out the fundamental ambiguity of the syntax, and after spending gallons 
of electrons arguing about it, the proponents pretty much backed off. But key 
paths don't have that problem—they always work the same way and are completely 
unambiguous, so there's no scope for misinterpretation. And the cases key paths 
can handle appear to be about as common as the cases placeholder syntax was 
intended for.

Nor is there a strong argument that the suggested behavior is fundamentally 
"weird". It's pretty natural to think of a `KeyPath` as being a 
`(Root) -> Value` function, and the `foo.map(\.bar)` syntax reads pretty 
straightforwardly as long as you know what `map` does.

There's one more reason I think we should do this. It is not about the 
technology; it is not even really about the ergonomics. It's more about 
language "marketing", for lack of a better term.

I think we were all surprised by the SE-0110 backlash. But in hindsight, I 
think it's pretty easy to explain. During the Swift 3 and 4 cycles, we 
systematically stripped conveniences and sugar from higher-order functions. We 
have good reasons to do this; we want to pare things down, fix the foundations, 
and then build up new conveniences. Eat your vegetables now and you can have 
dessert later.

But we're entering our second year of eating nothing but vegetables. It's very 
difficult to maintain a strict diet forever, especially when—like the vast 
majority of Swift's users who don't participate i

Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-06 Thread Benjamin Herzog via swift-evolution
Thanks for the feedback! I also like the idea where this is pivoting and would 
like to think deeper about the topic and possible solutions. I will change the 
proposal and polish it.
I would still like to use this thread to get more input if there is more.

__

Benjamin Herzog

> On 6. Jul 2017, at 01:15, Xiaodi Wu via swift-evolution 
>  wrote:
> 
> My initial reaction when this idea was floated was positive, but after Dave 
> and others' astute observations, I have come to the same opinion as him. 
> Namely, it seems hard to justify adding an overload that's purely syntactic 
> sugar, multiplying the number of ways to express the same thing, for a result 
> where the increase in readability is debatable.
> 
> Agree also that _if_ this is worthwhile, then keypaths as a subtype of the 
> corresponding function type would be the best way to go.
> 
> 
> On Wed, Jul 5, 2017 at 4:23 PM, Dave Abrahams via swift-evolution 
> mailto:swift-evolution@swift.org>> wrote:
> 
> on Wed Jul 05 2017, Benjamin Herzog  > wrote:
> 
> > Hey guys,
> >
> > I would like to pitch a small convenient change to the Swift stdlib.
> > With KeyPaths added in SE-0161 I would like to add some convenience
> > calls to map, flatMap and filter in Sequences. To extract properties of
> > an array of objects we currently use trailing closure syntax together
> > with the shorthand $0 for the first closure argument. This is still kind
> > of verbose and also hard to read in some situations.I think it is much 
> > better to understand what is
> > going on when using the
> > type safe KeyPaths for that. I already implemented a working solution
> > and would like to pitch the idea here to get some feedback before
> > opening the swift evolution proposal.I propose using
> >
> > persons.flatMap(keyPath: \.name)
> >
> > over
> >
> > persons.flatMap { $0.name  }
> >
> > Link to pull request: https://github.com/apple/swift/pull/10760 
> > 
> 
> Repeating my earlier response to this idea:
> 
> I am not convinced this syntactic sugar is worth complicating the
> language or library for, but if it is, IMO the right thing is to make a
> keypath be-a subtype of the appropriate function type, rather than to
> start down the path of creating a keypath overload for every method that
> takes a closure argument.
> 
> --
> -Dave
> 
> ___
> 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



signature.asc
Description: Message signed with OpenPGP
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-06 Thread Dave Abrahams via swift-evolution

on Wed Jul 05 2017, Brent Royal-Gordon  wrote:

>> On Jul 5, 2017, at 2:23 PM, Dave Abrahams via swift-evolution 
>>  wrote:
>> 
>> I am not convinced this syntactic sugar is worth complicating the
>> language or library for,
>
> For what it's worth, I think it is *totally* worth complicating the
> language or library. The manual alternative involves a ton of
> obfuscating code and punctuation:
>
>   person.map { $0[keyPath: myKeyPath] }   // Don't tell me this isn't 
> horrible.

It isn't horrible to me, except for the name “myKeyPath” ;-).

I'm not sure what you're objecting to about this.  Is it the very
appearance of curly braces?  There is not a world in which all of your
map's can be written by passing something that already has a concise
name, so closures will arise.  I personally find a closure much easier
to process mentally than some of the instances of

  person.map(someName.or.other)

I've seen.  They're more compact, but less general, and for me at least,
it takes an extra half-second or so to understand maps in the latter
form.  I tend to be cautious about writing higher-order functional code
without writing out closures for this reason.

>> but if it is, IMO the right thing is to make a
>> keypath be-a subtype of the appropriate function type, rather than to
>> start down the path of creating a keypath overload for every method that
>> takes a closure argument.
>
> However, I think making `KeyPath` a subtype of `(Foo) ->
> Bar` is a great way of handling this whole problem in one fell
> swoop—as long as it doesn't get delayed three or four versions while
> we clean up the type system.

I think you may underestimate the cost of releasing something you don't
really want in the long run.  Features are hard to take back (and
increasingly so as we converge on stability).  This one could easily
become much bigger than it is, because we'd have no reason not to do the
same thing for every other closure argument everywhere.  Personally, I
can't imagine that this bit of (very nice, but still) syntactic sugar
would take priority over the long-term health of the language.

-- 
-Dave
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-05 Thread Brent Royal-Gordon via swift-evolution
> On Jul 5, 2017, at 2:23 PM, Dave Abrahams via swift-evolution 
>  wrote:
> 
> I am not convinced this syntactic sugar is worth complicating the
> language or library for,

For what it's worth, I think it is *totally* worth complicating the language or 
library. The manual alternative involves a ton of obfuscating code and 
punctuation:

person.map { $0[keyPath: myKeyPath] }   // Don't tell me this isn't 
horrible.

> but if it is, IMO the right thing is to make a
> keypath be-a subtype of the appropriate function type, rather than to
> start down the path of creating a keypath overload for every method that
> takes a closure argument.

However, I think making `KeyPath` a subtype of `(Foo) -> Bar` is a 
great way of handling this whole problem in one fell swoop—as long as it 
doesn't get delayed three or four versions while we clean up the type system.

-- 
Brent Royal-Gordon
Architechies

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-05 Thread Xiaodi Wu via swift-evolution
My initial reaction when this idea was floated was positive, but after Dave
and others' astute observations, I have come to the same opinion as him.
Namely, it seems hard to justify adding an overload that's purely syntactic
sugar, multiplying the number of ways to express the same thing, for a
result where the increase in readability is debatable.

Agree also that _if_ this is worthwhile, then keypaths as a subtype of the
corresponding function type would be the best way to go.


On Wed, Jul 5, 2017 at 4:23 PM, Dave Abrahams via swift-evolution <
swift-evolution@swift.org> wrote:

>
> on Wed Jul 05 2017, Benjamin Herzog  wrote:
>
> > Hey guys,
> >
> > I would like to pitch a small convenient change to the Swift stdlib.
> > With KeyPaths added in SE-0161 I would like to add some convenience
> > calls to map, flatMap and filter in Sequences. To extract properties of
> > an array of objects we currently use trailing closure syntax together
> > with the shorthand $0 for the first closure argument. This is still kind
> > of verbose and also hard to read in some situations.I think it is much
> better to understand what is
> > going on when using the
> > type safe KeyPaths for that. I already implemented a working solution
> > and would like to pitch the idea here to get some feedback before
> > opening the swift evolution proposal.I propose using
> >
> > persons.flatMap(keyPath: \.name)
> >
> > over
> >
> > persons.flatMap { $0.name }
> >
> > Link to pull request: https://github.com/apple/swift/pull/10760
>
> Repeating my earlier response to this idea:
>
> I am not convinced this syntactic sugar is worth complicating the
> language or library for, but if it is, IMO the right thing is to make a
> keypath be-a subtype of the appropriate function type, rather than to
> start down the path of creating a keypath overload for every method that
> takes a closure argument.
>
> --
> -Dave
>
> ___
> 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


Re: [swift-evolution] [Pitch] KeyPath based map, flatMap, filter

2017-07-05 Thread Dave Abrahams via swift-evolution

on Wed Jul 05 2017, Benjamin Herzog  wrote:

> Hey guys,
>
> I would like to pitch a small convenient change to the Swift stdlib.
> With KeyPaths added in SE-0161 I would like to add some convenience
> calls to map, flatMap and filter in Sequences. To extract properties of
> an array of objects we currently use trailing closure syntax together
> with the shorthand $0 for the first closure argument. This is still kind
> of verbose and also hard to read in some situations.I think it is much better 
> to understand what is
> going on when using the
> type safe KeyPaths for that. I already implemented a working solution
> and would like to pitch the idea here to get some feedback before
> opening the swift evolution proposal.I propose using 
>
> persons.flatMap(keyPath: \.name)
>
> over
>
> persons.flatMap { $0.name }
>
> Link to pull request: https://github.com/apple/swift/pull/10760

Repeating my earlier response to this idea:

I am not convinced this syntactic sugar is worth complicating the
language or library for, but if it is, IMO the right thing is to make a
keypath be-a subtype of the appropriate function type, rather than to
start down the path of creating a keypath overload for every method that
takes a closure argument.

-- 
-Dave

___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution