[swift-evolution] [Pitch] Support for pure functions. Part n + 1.

Matthew Johnson matthew at anandabits.com
Tue Feb 21 21:44:58 CST 2017


> On Feb 21, 2017, at 9:38 PM, Michel Fortin <michel.fortin at michelf.ca> wrote:
> 
>> Le 21 févr. 2017 à 22:05, Matthew Johnson <matthew at anandabits.com> a écrit :
>> 
>>>> Le 20 févr. 2017 à 12:17, Matthew Johnson <matthew at anandabits.com> a écrit :
>>>> 
>>>>> 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.
>>> 
>>> Similar, yes. But more complicated too. In pseudo code, this is what you'd have to express for `Set.insert`:
>>> 
>>> 	pure(where: Element.hashValue is pure, Element.== is pure)
>>> 	func insert(_ element: Element) { ... }
>>> 
>>> Then the compiler can enforce that `insert` only does things that are allowed in a pure function, but can ignore the purity of Element.hashValue and Element.== because the caller will check for that that.
>> 
>> Yep, that’s the kind of thing I had in mind.
>> 
>> I can imagine being this explicit could get pretty cumbersome though.  I wonder if there is a way to streamline this.  For example, in this case if we could just talk about `Element`’s conformance to `Hashable` and we could talk about a `pure` conformance (all requirements are met with pure implementations) then we could just say something like this:
>> 
>> pure(Element.Hashable)
>> func insert(_ element: Element) { ... }
>> 
>> Note: I also removed some redundancies - we could just say that the parameters are a list of things the purity of which `insert`’s purity depends.
>> 
>> For function arguments it would look something like this:
>> 
>> pure(transform)
>> func map<T>(transform: Element -> T)
> 
> The approach is interesting and less heavy than my pseudo code example. The syntax with protocols is a bit off though. What does `Element.Hashable` mean in Swift? It refers to a member inside of `Element`, not a conformance of `Element` to the `Hashable` protocol. I don't think there is any syntax to refer to a conformance of a certain type to a protocol in Swift. The closest is a check for the existence of a conformance for generic arguments.

Right, this was pseudo code and I just used the first syntax for referencing a conformance that came to mind.  We would obviously need to do a solid round of design of the real syntax.  I was mostly trying to show an approach we might be able to sue keep the annotations manageable.

> 
> But I'd tend to gravitate towards your last example, which is simpler: just provide a path to the function that must be pure. Ideally you would be able do it like this:
> 
> 	pure(Element.hashValue, Element.==)
> 
> Unfortunately, it's hard (impossible?) currently to represent the getter of a property or to get the function associated with an operator like ==, so this is a bit ad-hoc. (Try to assign the function to a variable to see what I mean.) There's still some general language improvements required to make this work.
> 
> If you want another syntax requiring all functions in a protocol conformance to be pure, that's an additive thing. I'm not sure it's worth its weight though.

Yeah, it’s hard to say.  I just feel like referencing individual functions and properties is going to be a significant burden in some cases.  I’m trying to think of how we might be able to keep it under control.  If we can’t then people won’t want to bother with the annotation even when their function should be pure.

> 
> I do agree that it's cumbersome to have these complicated annotations. We could imitate D and have generic functions infer their purity automatically, but that wouldn't work well in a language like Swift where generic code can be opaque. Instead, perhaps the compiler could automatically suggest what to add when it detects some requirements are missing in a pure function.

I think it’s worth investing effort into trying to find a way to avoid burdensome annotations rather than having the compiler generate the boilerplate for us.

> 
> 
> -- 
> Michel Fortin
> https://michelf.ca
> 



More information about the swift-evolution mailing list