@pure function attribute

Swift lacks for semantic information if a function is free of side effects or not. Adding a @pure function attribute would give compile time information about a function being free of side effects. For a @pure function, the compiler would need to verify that the function is free of side effects (wich would be possible because of the @pure annotation of other functions).

The current api for the map function for collections is:

func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

Adding @pure would look like this:

func map<T>(_ transform: @pure (Element) throws -> T) rethrows -> [T]

Benefits:

  • api information expressed with language semantics
  • possible compile time optimizations
12 Likes

I like any proposal that introduces more referential transparency into programs so a big +1 from me! However, my primary concern with an effects system is that some side-effects are treated as non-side-effects by us, and Iā€™m thinking primarily of logging. For example, a map invocation invoking any method that does some logging (even conditionally as part of an error routine) would be illegal or would require an opt-out from the pure annotation. Weā€™d probably need to work out an opt-out mechanism but without introducing heavy syntax.

1 Like

I agree that there should be some exceptions, but especially for logging I do not see the need. Logging should log effects and not pure calculations.
For debugging there would need to be some exception - but that could be really easy:

At some level, the @pure annotation has to start - some primitives has to be annotated without the compiler check. Some none pure build in functions could use @pure on purpose, for example print.

9 Likes

As an aside, although map in Swift is inspired by operations of that name in functional languages, it is explicitly not limited to ā€˜pureā€™ transforms. Nor would it be possible to make it so, given ABI stability guarantees.

What would have been possible (and might still be possible) is for map to be ā€œre-pure,ā€ in the sense that it is pure if the transform is pure.

I would urge you to spend some time reading through the multiple preceding threads to see the extensive discussion already conducted on this topic. There is no need to start over from scratch; what would be helpful is if you could summarize what you read in those threads so that we can avoid repeating the same things over and over but instead make progress!

14 Likes

My earlier exploration of this topic: Exploratory proposals: Pure & Concurrent

I agree, I should have spent some time to check if other pitches existed before. I will read them and summarize them.

6 Likes

I also spent some time exploring this topic: Value semantics and pure functions: referential transparency for Swift.

3 Likes

Just curious if you have a citation here? I had just assumed it is implicitly not limited to pure transformations because Swift doesn't have any ability to do so (though @pure would aid in this).

1 Like

You and I have had this exact conversation before:

5 Likes

Ha! My spouse does this to me all the time, but without benefit of an electronic archive and search function. (sorry for the interruption; couldnā€™t help myself)

5 Likes

Though the proposal sounds interesting but my only concern is how is it going to make sure that the functions being passed are pure or the functions which have @pure annotations are actually pure. What if there are side effects due to multithreaded environment? Is it possible to create A generic annotation that can test the function for all kinds of side effects without knowing the different use cases of the functions that users are going to use?

Hi Ishan,

first we need to think about what pure means. Producing side effects is unavoidable at same level.

When we talk about pure functions we mean mathematical functions like f(x) = y. But even this type of function produces side effect: The computer changes memory to execute a function like this - it changes the state of something. Executing the function twice produces 2 different states in you computers memory.
The good news is, we do not care about these types of side effects. They do not affect the domain we want to avoid side effects for.

So our definition could be:

  1. The only purpose of a pure function is to calculate its output.
  2. The output only depends on its input.
  3. The same input always produces the same output.

According to this, the compiler would need to check if a @pure function only executes code that is pure. Since this is a recursive definition, it needs to start somewhere. At some level, we just need to tell the compiler: this is pure, don't check it. For example, the typical math operators (+, -, *, /) are pure and could be annotated with an unchecked @pure. After that every other @pure function could use these and the compiler could verify the pureness of these functions.

Greetings,
Johannes

2 Likes

In my opinion, pure functions in Swift should:

  • not read or write any global or static mutable state, or any other state not passed explicitly as input arguments
  • not call functions that are not also pure
  • not be overridden by an impure function (but could themselves override impure functions)
  • not perform I/O

I guess they could mutate inout parameters, allocate memory, throw, mutate reference types that are explicitly passed as arguments, call function-private inner functions, even if theyā€™re impure (since their context is pure), etc.

3 Likes

In the strictest sense, a pure function couldnā€™t even print debug messages, read or write the floating point mode flags (even if the flags are reset before the function exits), etc. I think we should opt for a somewhat laxer definition than that.

1 Like

Although I have not fully thought through the following, I think thereā€™s an opportunity here for Swift purity to include non mutating methods on value value types ā€” these could fit the above definition in every way except for the fact that self is an implicit argument to the function (generally, I know you can pass that argument explicitly but you donā€™t explicitly define it as an argument).

[edit] I do not mean to say that being non-mutating is enough to state purity in-and-of-itself. I just mean Iā€™m not sure being a method needs to exclude a function from consideration.

I very much want explicit support for purity, so this have +1 from me.
Iā€™m not sure I'm on board with @pure attribute. In a well structured data oriented code the majority of functions will end up being pure. This will add a lot of boilerplate purity declarations. So, there should be something else.

@Joe_Groff has talked about introducing a new => arrow to indicate purity without the boilerplate.

1 Like

Should there be any indication at the call site that you're invoking a pure function? (Apologies if this has already been discussed in one of the other threads.)

No, there is no reason for that. The point is to disallow pure functions from calling impure functions.

1 Like