[swift-evolution] [Pitch] Support for pure functions. Part n + 1.
matthew at anandabits.com
Mon Feb 20 11:17:55 CST 2017
> On Feb 20, 2017, at 10:55 AM, Michel Fortin via swift-evolution <swift-evolution at swift.org> wrote:
> (continuing my thoughts from my previous post)
> I think a definition of `pure` that'd work well for Swift is one that is based on forbidding dereferencing of pointers (including object references) so that all you ever deal with is value semantics. With some exceptions for dereferencing when value semantics are preserved. Here's how it could work inside a pure function:
> a) Structs/Locals:
> Structs and local variables behave similarly. You can access `let` and `var` properties and mutate the later.
> b) Classes:
> You can't access the variables of a class in a pure function. But you can access its `let` properties. That's because as long as there is no `var` in the dereferencing path, you are guarantied to be accessing a constant. In classes, `let` properties are thus implicitly pure; stored `var` properties are not. Which means that pure instance methods on classes can only access `let` properties, in addition to computed properties that are themselves `pure` and other `pure` methods.
> c) Classes (uniquely referenced):
> When a class is uniquely referenced (which you can check at runtime with `isUniquelyReferenced`), then it's safe to access its `var` properties within a pure function. There is no compiler-enforced system to deal with that however, so you'll need a way to vouch for this. Instead of vouching, a safety mechanism could be built for that by statically enforcing cases where a class is known to be uniquely referenced.
That’s an interesting idea.
> d) Copy on Write:
> COW is built on top of uniquely referenced classes. COW also needs read access to the storage when not uniquely referenced (so you can make a copy) which, again, would require vouching. Instead of vouching, a safety mechanism could be built to statically enforce that some instance variables (those that need to be copied) are only writable when the class is uniquely referenced but can otherwise be read at any time (perhaps by adding an attribute to those variables). This protects the pure function from external interference.
This sounds like a promising approach.
> e) Generic Containers:
> Generic containers that impose requirements on their elements will pose some additional problems, for instance: `Set.insert` needs to call `hashValue` and `==` on its elements, making the purity of `Set.insert` constrained by the purity of those functions. Without a way to express this kind of conditional purity, `Set` and `Dictionary` cannot be pure.
Could we use a mechanism similar to `rethrows` to address this kind of transitive purity? We have already discussed something along those lines for handling functions passed as arguments. Maybe that mechanism could be designed to handle this use case as well.
> I feel enforcement for (c) and (d) needs to be implemented first if you are to implement `pure` this way so you aren't over-reliant on vouching at every corner. Perhaps you won't even need a vouching mechanism once that's done.
>> Le 20 févr. 2017 à 11:24, Michel Fortin via swift-evolution <swift-evolution at swift.org> a écrit :
>> Le 20 févr. 2017 à 1:19, David Sweeris <davesweeris at mac.com> a écrit :
>>> On Feb 19, 2017, at 21:19, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>> This is very, very interesting. Thank you so much for the text.
>>>> If I understand your take correctly, the benefits of `pure` in Swift would be contingent on how pervasively it can be used (as it's the composability of pure functions that gives it exponential value). And, based on your discussion, very few functions in Swift would be compiler-provably pure [...]
>>> I think this might, at least partly, be because we can't restrict generic parameters to be value types.
>> That's somewhat a problem.
>> But think a bit about the copy-on-write containers. Because the compiler itself has no notion of copy-on-write semantics, the compiler can't prove that COW types behave correctly as value types. The optimizer would have to make the pessimistic assumption that dereferencing the pointer is possibly a dependence on external state, making the function unoptimizable. Basically, any struct with a pointer (or object reference) would need to be vouched for with a "trust me" attribute, or not be vouched for if it doesn't fit value semantics.
>> And, as you say, generic containers value-typeness will depend on their generic arguments.
>> But... keep in mind that for the purpose of evaluating whether a function is pure (as in strongly, optimizable pure), what matters really is not the type but what you do with it. `Optional<AnyObject>` is not what you would call a value type, but it does behave as a value type as long as you only compare it to `nil`, since you never access the object.
>> D doesn't make this fine-grained distinction and is fine in most cases because it has transitive immutability. Swift doesn't have that, so maybe we should try something a bit different.
>> Michel Fortin
>> swift-evolution mailing list
>> swift-evolution at swift.org
> Michel Fortin
> swift-evolution mailing list
> swift-evolution at swift.org
More information about the swift-evolution