> 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 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'. > 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
