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


(TJ Usiyan) #1

# Pure Functions

* Proposal: [SE-NNNN](
https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their
input and–as such–do not and should not reference any variables other than
those passed in. These same functions are not meant to have any effects
other than the aforementioned transformation of input. Currently, Swift
cannot assist the developer and confirm that any given function is one of
these 'pure' functions. To facilitate this, this proposal adds syntax to
signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to
create its output solely based on the provided recognizer.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
func _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

if `_computeNullability(of:)` is recursive at all, there exists a real
potential to accidentally reference `self` in its body and the mistake,
depending on circumstance, can be terribly subtle. Converting
`_computeNullability(of:)` to a `static` function is an option but
obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure'
function, the developer gains assurance from the tooling that it doesn't
reference anything or cause any side effects.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted
everywhere `->` currently is. Members created using this kewyord must
follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such,
should not require an effect on existing code. It could be used to annotate
closures accepted by methods in the standard library such as `map`,
`filter`, and `reduce`. While this would fit well with their typical use,
such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent
for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions.
This would be a massively source breaking change and, as such, is unlikely
to have any feasibility. It is, however, the most clean semantically, in my
opinion.


(Jon Hull) #2

+1 for the idea of pure functions in swift. Seems like it would enable a lot of good optimizations (in some cases even just evaluating the function at compile time).

-1 on the specific notation. I would much rather just put the word ‘pure’ in front of ‘func’, the same way we put ‘mutating' in front of mutating functions… it seems to me like these are part of the same family.

I agree we should allow inout.

Thanks,
Jon

···

On Feb 16, 2017, at 9:03 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Sean Heber) #3

Doesn’t this break down if you can pass a reference as a parameter to a pure function? If that’s not allowed, I guess I must have missed it. Also this seems to require the function has a return value. I suppose generally a pure function without a return value wouldn’t make much sense - unless you pass it a reference.

l8r
Sean

···

On Feb 16, 2017, at 11:03 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #4

In essence this is about assistance from the compiler that a function marked ‘pure’ is indeed pure?
I.e. an error message should be generated when a function marked as ‘pure’ is in fact not ‘pure’?

If the answer to both questions is ‘yes’ then -not surprising- its a -1 from me.

Unless there are other benefits?

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 16 Feb 2017, at 18:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Hart) #5

This looks cool. Quick nit-pick inline:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

```
class Recognizer {
  var nullabilityMemo: Bool?
  var isNullable: Bool {
    pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}

You still use `pfunc` in the example above. Shouldn’t it be using => ?

···

On 16 Feb 2017, at 18:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

    if let back = nullabilityMemo {
      return back
    } else {
      let back = _computeNullability(of: self)
      nullabilityMemo = back
      return back
    }
  }
}
```

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dennis Weissmann) #6

Big +1 from me for the concept, thanks for proposing this, here's some "but"'s that came to my mind after a quick read:

I don't quite like the introduction of a new function arrow, in the end (in my opinion) this is just a more special, more constrained version of a normal function. For me, "pure" feels more like an attribute, describing how a function differs from a "normal" function, see @escaping, etc. (I could imagine having an @total too, but I think the implementation of that is a bit more involved :-)) Have you thought about `@pure`? If so why did you decide against it?

Secondly, are they inherited? So if ClassA has a pure function a(), can I override it in subclass ClassB: ClassA to be impure? (I think no)
Can I annotate a function in a protocol to force it to be pure? (not sure about this one)

- Dennis

···

On Feb 16, 2017, at 6:03 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michel Fortin) #7

I'm just going to point out to the documentation about pure functions in the D language which has an unusual but very practical take on enforcing purity. It's interesting, and it might also provide a list for things that might need be addressed in this proposal.
https://dlang.org/spec/function.html#pure-functions

···

Le 16 févr. 2017 à 12:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> a écrit :

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Michel Fortin
https://michelf.ca


(Haravikk) #8

I like the idea of having pure functions in Swift, but my first thought is; should we have to declare it at all? Is it not easier to just have the compiler automatically flag a function as pure or not?

With that in mind we don't need any new syntax, but a simple @pure attribute should be sufficient. This can be used anywhere that a function is declared, or a closure is accepted as a parameter, allowing us to be explicit that we are trying to define a pure function, or only accept pure closures.

The big benefit of this is that it is retroactive; all existing functions that are pure will be automatically detected as such, and can be passed into any method accepting only pure functions. The new capability will be that developers can specify that a function *must* be pure and thus produce an error if it isn't.

···

On 16 Feb 2017, at 17:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michel Fortin) #9

I'm a bit disappointed to see this discussion bikeshedding the syntax without clarifying much the semantics. Here's a few question of semantic nature.

The base principle of a pure function is that it has no side effects. But it's quite clear that at least some side effects will need to be allowed. So which ones?

1. Many basic calculations will leave flags in registers that persist until the next computation. Those can be checked by the program later (after the function is run in some cases). That's probably a side effect you want to ignore.

2. Floating point operations can give different results depending on flags you can set in the registers. Things like setting the rounding mode for instance. Will the pure function need to reset those flags before performing floating point operations so it can guaranty the same result every time? What are the implication if those are ignored?

3. Is a pure function allowed to dereference pointers or object references passed as parameters? A pointer or an object reference might provide access to the global state of the program.

4. Allocating memory requires access to the global memory layout of the program. And allocating will give you a different memory address every time. Are you allowed to allocate memory in a pure function?

5. Many basic operations in the language will implicitly allocate memory or dereference pointers. Same for the containers in the standard library. If you don't allow memory allocations or pointer dereferencing, what subset of the language is still usable in a pure function?

6. If you do allow memory allocations inside the function, is it safe to instantiate and return a new class?

7. Is it desirable that the optimizer sometime take the pure attribute to heart to combine multiple apparently redundant calls into a single one? Or is pure not intended to be usable for compiler optimizations? The ability to optimize will likely be affected by the answer to these question and the loopholes you are willing to allow.

8. Is exiting the program (`fatalError`, etc.) allowed? That's a pretty big side effect. Although it could also be considered as no side effect since the program cannot go further.

9. Is throwing allowed? Maybe the thrown error should considered simply as different return value.

10. Is += allowed inside a pure function? Operators are functions too, but can += be made pure with `inout` and no return value?

11. Can you use a BigInt implementation in a pure function? BigInt needs to allocate internally.

13. Say you want to keep logs of what happens in the function in order to debug something: is there an "unsafe" way to do IO for that purpose? Or maybe you want to implement `fatalError` as a pure function: is there a way to print something before exiting?

Basically, there is no such thing as "no side effect". You need to pick which side effects are acceptable and then evaluate where your picks puts you on the scale between too lax (and of little utility) and too restrictive (and not usable outside of trivial examples). It might be a good idea to make the answer to all those questions clear in the proposal so people have an idea of what they can expect pure functions to do and what guaranties they provide.

···

--
Michel Fortin
https://michelf.ca


(David Sweeris) #10

Could we use pure functions as the basis of "type functions"? That is, functions that operate on, and return, types rather than variables. For example, the "N+P" part of the return type in:
    func append <M,N,P> (lhs: Matrix<M,N>, rhs: Matrix<M,N,P>) -> Matrix<M, N+P> {...}
(where M,N, and P all conform to "Length" or something, since Int literals can't be generic parameters yet.) Because something like that would come in real handy for matrix and vector types.

I know this feature would have to be a separate proposal... I was just thinking of potential applications of pure functions, since somebody was asking earlier in the thread.

- Dave Sweeris

···

On Feb 16, 2017, at 09:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.


(David Sweeris) #11

If it mutates whatever the input is referencing, it would have a side-effect which makes it "not pure" (for my understanding of what “pure” means).

- Dave Sweeris

···

On Feb 16, 2017, at 11:27 AM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

Doesn’t this break down if you can pass a reference as a parameter to a pure function? If that’s not allowed, I guess I must have missed it. Also this seems to require the function has a return value. I suppose generally a pure function without a return value wouldn’t make much sense - unless you pass it a reference.


(Robert Widmann) #12

In essence this is about assistance from the compiler that a function marked ‘pure’ is indeed pure?
I.e. an error message should be generated when a function marked as ‘pure’ is in fact not ‘pure’?

If the answer to both questions is ‘yes’ then -not surprising- its a -1 from me.

Unless there are other benefits?

This feature already exists <https://github.com/apple/swift/blob/master/docs/proposals/OptimizerEffects.rst> in a certain sense - via the @effects annotation - but is undocumented, highly unstable, and does not entail any semantic checking. I think that at least indicates a desire, even if it’s only in the lower-level parts of SIL now, to have some way to determine the “purity” of a function to perhaps guide an inliner or future block fusion pass. Perhaps Andrew Trick can speak more about the goals of the annotation and whether it would be ready for prime time as it were.

···

On Feb 16, 2017, at 12:30 PM, Rien via swift-evolution <swift-evolution@swift.org> wrote:

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 16 Feb 2017, at 18:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #13

Secondly, are they inherited? So if ClassA has a pure function a(), can I override it in subclass ClassB: ClassA to be impure? (I think no)

I would agree. IIUC, the "pureness" of A's implementation of a() becomes a moot point if B's implementation doesn't have to have at least as strong of a constraint, because otherwise all the optimizations that the compiler makes would break as soon as someone passes a B in instead of an A.

Can I annotate a function in a protocol to force it to be pure? (not sure about this one)

I would say yes, for similar reasons: if the attribute doesn't hold for *all* implementations, the compiler can't assume it holds for any. At least not with the way generics work now, IIUC.

- Dave Sweeris.

···

On Feb 16, 2017, at 13:10, Dennis Weissmann via swift-evolution <swift-evolution@swift.org> wrote:


(Joe Groff) #14

Effects is too low-level to be useful for much user code. It blindly applies LLVM attributes that are difficult for most Swift code to live up to. Any value type with refcounted fields immediately fails the "readnone" test even if it's semantically pure at the value semantics abstraction level. I think a higher-level 'pure' annotation like TJ is proposing, which fits into the value semantics model, is more useful as a user-facing feature, both as a reasoning tool and as an enabler for higher-level optimization.

-Joe

···

On Feb 16, 2017, at 9:51 AM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 16, 2017, at 12:30 PM, Rien via swift-evolution <swift-evolution@swift.org> wrote:

In essence this is about assistance from the compiler that a function marked ‘pure’ is indeed pure?
I.e. an error message should be generated when a function marked as ‘pure’ is in fact not ‘pure’?

If the answer to both questions is ‘yes’ then -not surprising- its a -1 from me.

Unless there are other benefits?

This feature already exists in a certain sense - via the @effects annotation - but is undocumented, highly unstable, and does not entail any semantic checking. I think that at least indicates a desire, even if it’s only in the lower-level parts of SIL now, to have some way to determine the “purity” of a function to perhaps guide an inliner or future block fusion pass. Perhaps Andrew Trick can speak more about the goals of the annotation and whether it would be ready for prime time as it were.


(TJ Usiyan) #15

I am ok with a keyword but `pure` in front of func doesn't work well with
inline closures.

A few people talked through many of these issues starting with this tweet.
https://twitter.com/griotspeak/status/832247545325842432

···

On Thu, Feb 16, 2017 at 4:13 PM, Jonathan Hull <jhull@gbis.com> wrote:

+1 for the idea of pure functions in swift. Seems like it would enable a
lot of good optimizations (in some cases even just evaluating the function
at compile time).

-1 on the specific notation. I would much rather just put the word ‘pure’
in front of ‘func’, the same way we put ‘mutating' in front of mutating
functions… it seems to me like these are part of the same family.

I agree we should allow inout.

Thanks,
Jon

On Feb 16, 2017, at 9:03 AM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/
master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their
input and–as such–do not and should not reference any variables other than
those passed in. These same functions are not meant to have any effects
other than the aforementioned transformation of input. Currently, Swift
cannot assist the developer and confirm that any given function is one of
these 'pure' functions. To facilitate this, this proposal adds syntax to
signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant
to create its output solely based on the provided recognizer.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
func _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

if `_computeNullability(of:)` is recursive at all, there exists a real
potential to accidentally reference `self` in its body and the mistake,
depending on circumstance, can be terribly subtle. Converting
`_computeNullability(of:)` to a `static` function is an option but
obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure'
function, the developer gains assurance from the tooling that it doesn't
reference anything or cause any side effects.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted
everywhere `->` currently is. Members created using this kewyord must
follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such,
should not require an effect on existing code. It could be used to annotate
closures accepted by methods in the standard library such as `map`,
`filter`, and `reduce`. While this would fit well with their typical use,
such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain
consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions.
This would be a massively source breaking change and, as such, is unlikely
to have any feasibility. It is, however, the most clean semantically, in my
opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ross O'Brien) #16

I'm in favour of 'pure' functions, because they're a way of reducing the
repetition of the kind of calculations you might want to perform in
initialisers to convert argument values into property values.

I don't know why "the function must have a return type" is seen as a
requirement, though. There may not be many reasons to call a pure function
without a return type, but requiring a return type, or disallowing inout
parameters, seems arbitrary to me.

···

On Thu, Feb 16, 2017 at 5:51 PM, Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 16, 2017, at 12:30 PM, Rien via swift-evolution < > swift-evolution@swift.org> wrote:

In essence this is about assistance from the compiler that a function
marked ‘pure’ is indeed pure?
I.e. an error message should be generated when a function marked as ‘pure’
is in fact not ‘pure’?

If the answer to both questions is ‘yes’ then -not surprising- its a -1
from me.

Unless there are other benefits?

This feature already exists
<https://github.com/apple/swift/blob/master/docs/proposals/OptimizerEffects.rst> in
a certain sense - via the @effects annotation - but is undocumented, highly
unstable, and does not entail any semantic checking. I think that at least
indicates a desire, even if it’s only in the lower-level parts of SIL now,
to have some way to determine the “purity” of a function to perhaps guide
an inliner or future block fusion pass. Perhaps Andrew Trick can speak
more about the goals of the annotation and whether it would be ready for
prime time as it were.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 16 Feb 2017, at 18:03, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/
master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their
input and–as such–do not and should not reference any variables other than
those passed in. These same functions are not meant to have any effects
other than the aforementioned transformation of input. Currently, Swift
cannot assist the developer and confirm that any given function is one of
these 'pure' functions. To facilitate this, this proposal adds syntax to
signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant
to create its output solely based on the provided recognizer.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
func _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

if `_computeNullability(of:)` is recursive at all, there exists a real
potential to accidentally reference `self` in its body and the mistake,
depending on circumstance, can be terribly subtle. Converting
`_computeNullability(of:)` to a `static` function is an option but
obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure'
function, the developer gains assurance from the tooling that it doesn't
reference anything or cause any side effects.

class Recognizer {
var nullabilityMemo: Bool?
var isNullable: Bool {
pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
if let back = nullabilityMemo {
return back
} else {
let back =  _computeNullability(of: self)
nullabilityMemo = back
return back
}
}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted
everywhere `->` currently is. Members created using this kewyord must
follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such,
should not require an effect on existing code. It could be used to annotate
closures accepted by methods in the standard library such as `map`,
`filter`, and `reduce`. While this would fit well with their typical use,
such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain
consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions.
This would be a massively source breaking change and, as such, is unlikely
to have any feasibility. It is, however, the most clean semantically, in my
opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(André Videla) #17

+1

I think it's a very interesting proposal and here is why:

- It improve readability by expressing intent.
For example the difference between those two function, just by their signature, is obvious:

func update(_ m: Class) -> Class

and

func update(_ m: Class) => Class

The first one mutates the class passed in argument and returns it (for example, to allows chaining mutable methods).
And the second one returns a new reference without touching the original class passed in argument.
Or at least that is how I would expect it to work. (Maybe OP can confirm?)

- Though the compiler helps with captures it would give an additional safeguard again accidentally capturing the context or creating reference cycles.
For example, it could allow to define pure closures that escape their scope, without having them be marked @escaping:

func updater(fn: (Int) => Int, initial: Int) -> () -> Int {
    var i = initial
    return {
        i = fn(i)
        return i
    }
}
instead of

func updater(fn: @escaping (Int) -> Int, initial: Int) -> () -> Int {
    var i = initial
    return {
        i = fn(i)
        return i
    }
}

This would introduce some pretty interesting scoping rules, I'd be very interested to hear your opinion on this.

- *something something Stream Fusion*

André

···

On 16 Feb 2017, at 19:10, Ross O'Brien via swift-evolution <swift-evolution@swift.org> wrote:

I'm in favour of 'pure' functions, because they're a way of reducing the repetition of the kind of calculations you might want to perform in initialisers to convert argument values into property values.

I don't know why "the function must have a return type" is seen as a requirement, though. There may not be many reasons to call a pure function without a return type, but requiring a return type, or disallowing inout parameters, seems arbitrary to me.

On Thu, Feb 16, 2017 at 5:51 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 16, 2017, at 12:30 PM, Rien via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

In essence this is about assistance from the compiler that a function marked ‘pure’ is indeed pure?
I.e. an error message should be generated when a function marked as ‘pure’ is in fact not ‘pure’?

If the answer to both questions is ‘yes’ then -not surprising- its a -1 from me.

Unless there are other benefits?

This feature already exists <https://github.com/apple/swift/blob/master/docs/proposals/OptimizerEffects.rst> in a certain sense - via the @effects annotation - but is undocumented, highly unstable, and does not entail any semantic checking. I think that at least indicates a desire, even if it’s only in the lower-level parts of SIL now, to have some way to determine the “purity” of a function to perhaps guide an inliner or future block fusion pass. Perhaps Andrew Trick can speak more about the goals of the annotation and whether it would be ready for prime time as it were.

Regards,
Rien

Site: http://balancingrock.nl <http://balancingrock.nl/>
Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl <http://swiftfire.nl/>

On 16 Feb 2017, at 18:03, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

# Pure Functions

* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md)
* Author(s): [TJ Usiyan](https://github.com/griotspeak)
* Status: **Awaiting review**
* Review manager: TBD

## Introduction

Some functions are, essentially, only meant to be transformations of their input and–as such–do not and should not reference any variables other than those passed in. These same functions are not meant to have any effects other than the aforementioned transformation of input. Currently, Swift cannot assist the developer and confirm that any given function is one of these 'pure' functions. To facilitate this, this proposal adds syntax to signal that a function is 'pure'.

'pure', in this context, means:
1. The function must have a return value
1. This function can only call other pure functions
1. This function cannot access/modify global or static variables.

## Motivation

Consider the following example where `_computeNullability(of:)` is meant to create its output solely based on the provided recognizer.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		func _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

if `_computeNullability(of:)` is recursive at all, there exists a real potential to accidentally reference `self` in its body and the mistake, depending on circumstance, can be terribly subtle. Converting `_computeNullability(of:)` to a `static` function is an option but obfuscates the fact that it is *only* to be called within `isNullable`.

## Proposed solution

Given the ability to indicate that `_computeNullability(of:)` is a 'pure' function, the developer gains assurance from the tooling that it doesn't reference anything or cause any side effects.

class Recognizer {
	var nullabilityMemo: Bool?
	var isNullable: Bool {
		pfunc _computeNullability(of recognizer: Recognizer) -> Bool {…}
		if let back = nullabilityMemo {
			return back		
		} else {
			let back =  _computeNullability(of: self)
			nullabilityMemo = back
			return back
		}
	}
}

## Detailed design

This proposal introduces a new annotation `=>`, which is to be accepted everywhere `->` currently is. Members created using this kewyord must follow the rules listed in the introduction.

## Impact on existing code

This is an additive feature unless alternative 2 is chosen and, as such, should not require an effect on existing code. It could be used to annotate closures accepted by methods in the standard library such as `map`, `filter`, and `reduce`. While this would fit well with their typical use, such a change is not necessarily part of this proposal.

## Alternatives considered

It should be noted that neither of these alternatives can remain consistent for inline closures.
1. keyword `pfunc` (pronounciation: pifəŋk) for 'pure' functions.
2. `proc` keyword for 'impure' functions and 'func' for 'pure' functions. This would be a massively source breaking change and, as such, is unlikely to have any feasibility. It is, however, the most clean semantically, in my opinion.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Nicolas Fezans) #18

If it mutates whatever the input is referencing, it would have a

side-effect which makes it "not pure" (for my understanding of what “pure”
means).

I am not really sure of it (I have not played around with it until now) but
I don't think that this is an issue with the swift inout, cf.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545

Nicolas

···

On Thu, Feb 16, 2017 at 8:55 PM, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

> On Feb 16, 2017, at 11:27 AM, Sean Heber via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Doesn’t this break down if you can pass a reference as a parameter to a
pure function? If that’s not allowed, I guess I must have missed it. Also
this seems to require the function has a return value. I suppose generally
a pure function without a return value wouldn’t make much sense - unless
you pass it a reference.

If it mutates whatever the input is referencing, it would have a
side-effect which makes it "not pure" (for my understanding of what “pure”
means).

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dennis Weissmann) #19

Secondly, are they inherited? So if ClassA has a pure function a(), can I override it in subclass ClassB: ClassA to be impure? (I think no)

I would agree. IIUC, the "pureness" of A's implementation of a() becomes a moot point if B's implementation doesn't have to have at least as strong of a constraint, because otherwise all the optimizations that the compiler makes would break as soon as someone passes a B in instead of an A.

Can I annotate a function in a protocol to force it to be pure? (not sure about this one)

I would say yes, for similar reasons: if the attribute doesn't hold for *all* implementations, the compiler can't assume it holds for any. At least not with the way generics work now, IIUC.

That makes a lot of sense and would be consistent with @escaping's behavior.

···

On Feb 16, 2017, at 10:24 PM, David Sweeris <davesweeris@mac.com> wrote:

On Feb 16, 2017, at 13:10, Dennis Weissmann via swift-evolution <swift-evolution@swift.org> wrote:

- Dave Sweeris.


(Charles Srstka) #20

Not only that, but even if you pass a value type as a parameter, that value type might have reference types as ivars. So since any call to any reference type can potentially mutate it, and any call to any value type could call through to a reference type which might then be mutated, it does seem that purity is quite difficult to guarantee.

Charles

···

On Feb 16, 2017, at 1:27 PM, Sean Heber via swift-evolution <swift-evolution@swift.org> wrote:

Doesn’t this break down if you can pass a reference as a parameter to a pure function? If that’s not allowed, I guess I must have missed it. Also this seems to require the function has a return value. I suppose generally a pure function without a return value wouldn’t make much sense - unless you pass it a reference.