> On Apr 6, 2017, at 4:39 PM, Joe Groff <[email protected]> wrote:
> 
>> 
>> On Apr 6, 2017, at 2:35 PM, Matthew Johnson <[email protected]> wrote:
>> 
>>> 
>>> On Apr 6, 2017, at 4:22 PM, Joe Groff <[email protected]> wrote:
>>> 
>>>> 
>>>> On Apr 6, 2017, at 2:15 PM, Matthew Johnson <[email protected]> wrote:
>>>> 
>>>> 
>>>>> On Apr 6, 2017, at 1:11 PM, Joe Groff <[email protected]> wrote:
>>>>> 
>>>>> 
>>>>>> On Apr 6, 2017, at 11:06 AM, John McCall <[email protected]> wrote:
>>>>>> 
>>>>>>> On Apr 6, 2017, at 1:41 PM, Matthew Johnson <[email protected]> 
>>>>>>> wrote:
>>>>>>>> On Apr 6, 2017, at 12:32 PM, John McCall <[email protected]> wrote:
>>>>>>>> 
>>>>>>>>> On Apr 5, 2017, at 9:46 PM, Matthew Johnson via swift-evolution 
>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>> On Apr 5, 2017, at 7:32 PM, David Smith via swift-evolution 
>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>> 
>>>>>>>>>> The rationale for using the same syntax is that a KeyPath is an 
>>>>>>>>>> unapplied property/subscript access. Even the multi-segment part of 
>>>>>>>>>> it isn't necessarily dissimilar: I don't think it would be 
>>>>>>>>>> unreasonable to imagine that \Foo.someMethod.someOtherMethod could 
>>>>>>>>>> work*, that'd just be function composition after all.
>>>>>>>>>> 
>>>>>>>>>> KeyPath : Properties/Subscripts :: Functions with a self argument : 
>>>>>>>>>> Methods
>>>>>>>>>> 
>>>>>>>>>>      David
>>>>>>>>>> 
>>>>>>>>>> *not proposing this, haven't thought carefully about whether there 
>>>>>>>>>> are edge cases I'm missing here, but I think the analogy holds
>>>>>>>>> 
>>>>>>>>> I alluded to this kind of thing in the earlier threads.  It would be 
>>>>>>>>> very cool to see this explored in the future.
>>>>>>>>> 
>>>>>>>>> I really like the latest draft and am eagerly anticipating Smart 
>>>>>>>>> KeyPaths being implemented.  Thank you for listening to feedback from 
>>>>>>>>> the community!
>>>>>>>>> 
>>>>>>>>> One possible future direction I have been wondering about is whether 
>>>>>>>>> it might be interesting to expose an anonymous type for each distinct 
>>>>>>>>> key path which would have static members for getting (and setting if 
>>>>>>>>> mutable) the value.  The types would inherit from the most specific 
>>>>>>>>> matching key path type included in this proposal. This would allow us 
>>>>>>>>> pass key paths statically using the type system and therefore not 
>>>>>>>>> requiring any runtime overhead.  
>>>>>>>>> 
>>>>>>>>> I have experimented with this approach in some of my own code and it 
>>>>>>>>> looks like it would be a very promising approach aside from the 
>>>>>>>>> boilerplate required to write these types manually.  I have abandoned 
>>>>>>>>> this approach for now because of the boilerplate and because the 
>>>>>>>>> syntactic sugar of the key path shorthand in this proposal is too 
>>>>>>>>> attractive to pass up.  I would love to explore it again in the 
>>>>>>>>> future if key paths were to support this approach.
>>>>>>>> 
>>>>>>>> Our generics system does not require generic code to be de-genericized 
>>>>>>>> ("instantiated" in C++ terminology, "monomorphized" in Rust, etc.) in 
>>>>>>>> order to be run. The generic code for applying a value of an unknown 
>>>>>>>> key-path type would look exactly like the non-generic code for 
>>>>>>>> applying a dynamic key-path type.  To get a runtime benefit, the 
>>>>>>>> compiler would have to de-genericize all the code between the function 
>>>>>>>> that formed the concrete key path and the function that applied it.  
>>>>>>>> If the compiler can do that, it can also specialize that code for a 
>>>>>>>> known key path argument, the same way that it can specialize a 
>>>>>>>> function for a known function argument.  So there's no point.
>>>>>>> 
>>>>>>> Thanks for the reply John.  There may not be any additional 
>>>>>>> optimization opportunities in terms of code generation when using the 
>>>>>>> key path but wouldn’t it save on storage and reference counting related 
>>>>>>> to key path value?
>>>>>> 
>>>>>> If you're specializing all the way down, any sort of boxing should be 
>>>>>> possible to eliminate as well.
>>>>>> 
>>>>>> If you mean in unspecialized code, well, I'm not entirely sure what 
>>>>>> representation Joe is using, but I would assume that the fast path — 
>>>>>> where a key path doesn't capture anything — does not require any 
>>>>>> allocation.  In that sense, there's a strong parallel with how we 
>>>>>> represent functions: yes, avoiding an extra allocation would be nice, 
>>>>>> but if you're willing to accept an occasional allocation in more complex 
>>>>>> cases, there are also a lot of benefits from being able to always give 
>>>>>> the type a concrete, fixed-size representation.
>>>>> 
>>>>> Yeah, I've set up the implementation so that literal key paths get 
>>>>> emitted as global objects that don't need allocation, and shouldn't need 
>>>>> refcounting once the runtime gains support for inert objects. I also set 
>>>>> up the SIL representation for these in a way that we'll eventually be 
>>>>> able to do high-level optimization, so that when we see a literal key 
>>>>> path applied to a concrete base, we can lower the key path application 
>>>>> into direct projections on the base value. Since everything is 
>>>>> implemented using opaque classes now, there isn't much opportunity for 
>>>>> specialization, but I think high-level optimizations are probably a 
>>>>> better fit for the common cases.
>>>> 
>>>> If I understand this correctly the storage for key path values that begin 
>>>> as a literal would be a non-refcounted word after inert object support is 
>>>> included in the runtime.  Is that correct?
>>> 
>>> The value representation will a pointer, the same as any class type, though 
>>> we can avoid dynamically allocating space for it if it isn't parameterized 
>>> at all. Information about the key path's contents has to live in the 
>>> process somewhere.
>> 
>> Can you clarify what you mean by “if it isn’t parameterized at all”?
> 
> If it doesn't capture subscript indexes, unspecialized generic arguments from 
> the environment, or anything like that that would require it to be 
> instantiated.

Thanks for explaining.  :)

> 
>> 
>>> 
>>>> Can you elaborate on the high-level optimizations you have in mind?
>>> 
>>> If you end up with 'x[keyPath: \.y]', whether directly or by constant 
>>> propagation, the compiler ought to be able to optimize that down to 'x.y’.
>> 
>> That makes sense.  Of course they won’t often be used like this - why not 
>> just write `x.y` in the first place?
> 
> You could end up with code that looks like that after inlining or other 
> optimizations. For instance, if you had:
> 
> for key in [\Vector.x, \.y, \.z, \.w] {
>       a[key] += b[key]
> }
> 
> We'd hopefully unroll the loop and propagate the constants from the literal 
> array, letting that boil down to `a.x += b.x; a.y += b.y; …'

Good example.  Thanks!

> 
> -Joe
> 
>>> 
>>>> Also, you said "Since everything is implemented using opaque classes now, 
>>>> there isn't much opportunity for specialization”.  Does that mean the 
>>>> implementation may change in the future in a way that might allow for more 
>>>> opportunity for specialization?
>>> 
>>> A design using generalized existentials would be a lot more expressive and 
>>> probably be able to exercise the compiler's generic optimization 
>>> infrastructure better.
>>> 
>>> -Joe

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to