pure functions

Splitting this out from the concurrency thread:

In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve.

This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve.

I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator.

My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library.

Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary.

To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems.

My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example.

The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

-Chris

···

On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> wrote:

Splitting this out from the concurrency thread:

In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve.

This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve.

I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator.

My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library.

Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary.

To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems.

My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example.

At a rough guess, how many of the stdlib's functions could realistically have their purity inferred?

If we define our semantics carefully enough, could we realistically make it so that "@pure" or "@impure" (or whatever) would simply be a note to the compiler, letting it skip that bit of analysis for the function in question?

The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

Did we ever get around to figuring which definition(s) of "pure" that we wanted to support, or did the discussion always get out of scope before we got that far? I know the issue's been brought up, but I don't recall if it was discussed enough to reach any conclusions.

- Dave Sweeris

···

On Aug 18, 2017, at 12:11, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> wrote:

Now that inout parameters are guaranteed exclusive, a mutating method on a struct or a function that takes inout parameters is isomorphic to one that consumes the initial value as a pure argument and returns the modified value back. This provides a value-semantics-friendly notion of purity, where a function can still be considered pure if the only thing it mutates is its unescaped local state and its inout parameters and it doesn't read or write any shared mutable state such as mutable globals, instance properties, or escaped variables. That gives you the ability to declare local variables and composably apply "pure" mutating operations to them inside a pure function.

We've already brought Swift somewhat into the effects-system design space with "throws" (and "async", if it gets taken as we've currently proposed it), and we already have some abstraction debt to pay off with "throws"; if we wanted to, we could conceivably fold "impure" into that system as well. While it's true that there would be a lot of effort in propagating pure annotations to the right places, that's going to be true of any attempt to move the current everything-goes world into a more robust and constrained framework. I don't think we should write off the notion completely.

-Joe

···

On Aug 18, 2017, at 12:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Splitting this out from the concurrency thread:

On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> wrote:

In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve.

This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve.

I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator.

My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library.

Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary.

To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems.

My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example.

The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

It became clear that we weren’t going to change Swift’s default semantics in the version 2/3 timeframe, and we made the effort to do sufficient whole-module analysis to make up for it. I see this primarily being a resilience issue in the future. Eventually I think we’ll want public APIs to be able to declare some level of statically enforced purity. We might even get some level of default guarantee as a way of “deprecating global var”. But that whole discussion has rightly been punted.

-Andy

···

On Aug 18, 2017, at 2:45 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

If we define our semantics carefully enough, could we realistically make it so that "@pure" or "@impure" (or whatever) would simply be a note to the compiler, letting it skip that bit of analysis for the function in question?

The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

Did we ever get around to figuring which definition(s) of "pure" that we wanted to support, or did the discussion always get out of scope before we got that far? I know the issue's been brought up, but I don't recall if it was discussed enough to reach any conclusions.

As Andy says, the major problem is that public API (which you can't see the implementation of in general) would all need to be marked up properly.

-Chris

···

On Aug 18, 2017, at 2:45 PM, David Sweeris <davesweeris@mac.com> wrote:

On Aug 18, 2017, at 12:11, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Splitting this out from the concurrency thread:

On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> wrote:
On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> wrote:

In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve.

This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve.

I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator.

My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library.

Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary.

To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems.

My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example.

At a rough guess, how many of the stdlib's functions could realistically have their purity inferred?

If we define our semantics carefully enough, could we realistically make it so that "@pure" or "@impure" (or whatever) would simply be a note to the compiler, letting it skip that bit of analysis for the function in question?

The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

Did we ever get around to figuring which definition(s) of "pure" that we wanted to support, or did the discussion always get out of scope before we got that far? I know the issue's been brought up, but I don't recall if it was discussed enough to reach any conclusions.

- Dave Sweeris

I agree with Joe here... in principle. We keep finding features we want to add that have effects-system-like semantics, and I expect that we will continue to do so. The best language design would probably result from addressing this category of feature in some holistic way. My main concern is that in practice, it seems unlikely to be doable before ABI lockdown makes it too late.

···

on Wed Aug 23 2017, Joe Groff <swift-evolution@swift.org> wrote:

On Aug 18, 2017, at 12:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote: Splitting this out from the concurrency thread:

On Aug 18, 2017, at 6:12 AM, Matthew Johnson >>> <matthew@anandabits.com> wrote:

On Aug 17, 2017, at 11:53 PM, Chris Lattner >>>> <clattner@nondot.org> wrote:

In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve. This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve. I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator. My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library. Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary. To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems. My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example. The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

Now that inout parameters are guaranteed exclusive, a mutating method on a struct or a function that takes inout parameters is isomorphic to one that consumes the initial value as a pure argument and returns the modified value back. This provides a value-semantics-friendly notion of purity, where a function can still be considered pure if the only thing it mutates is its unescaped local state and its inout parameters and it doesn't read or write any shared mutable state such as mutable globals, instance properties, or escaped variables. That gives you the ability to declare local variables and composably apply "pure" mutating operations to them inside a pure function.

We've already brought Swift somewhat into the effects-system design space with "throws" (and "async", if it gets taken as we've currently proposed it), and we already have some abstraction debt to pay off with "throws"; if we wanted to, we could conceivably fold "impure" into that system as well. While it's true that there would be a lot of effort in propagating pure annotations to the right places, that's going to be true of any attempt to move the current everything-goes world into a more robust and constrained framework. I don't think we should write off the notion completely.

--
-Dave

Is there anything in particular that would make it more likely to be doable before ABI lockdown? (Other that pushing it back again, I mean)

I'd guess that if it were as simple as adding a few "reserved for future use" bits in the on-disk representation, you wouldn't have raised the issue.

- Dave Sweeris

···

On Sep 9, 2017, at 10:48 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Aug 23 2017, Joe Groff <swift-evolution@swift.org> wrote:

On Aug 18, 2017, at 12:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote: Splitting this out from the concurrency thread:

On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> wrote:
In the manifesto you talk about restrictions on passing functions across an actor message. You didn’t discuss pure functions, presumably because Swift doesn’t have them yet. I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message. Is that correct?

Correct. The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways. The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve. This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system. There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve. I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way). You’re right though that this could be an interesting motivator.

I can provide a concrete example of why this is definitely and important motivator. My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code. This is working out really well and I plan to continue with this approach. The library level code needs the ability to schedule user code in the appropriate context. There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context. They are obviously not closure literals from the perspective of the library. Pure functions are obviously important to the semantics of this approach. We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified. That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary. To be clear, I am talking in the context of "the fullness of time". It would be perfectly acceptable to ship actors before pure functions. That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

Right. Pure functions are also nice when you care about thread safety, and there is a lot of work on this. C has __attribute__((const)) and ((pure)) for example, c++ has constexpr, and many research languages have built full blown effects systems. My principle concern is that things like this quickly become infectious: LOTS of things are pure functions, and requiring them all to be marked as such becomes a lot of boilerplate and conceptual overhead. This is happening in the C++ community with constexpr for example. The secondary concern is that you need to build out the model enough that you don’t prevent abstractions. A pure function should be able to create an instance of a struct, mutate it (i.e. calling non-pure functions) etc. This requires a non-trivial design, and as the design complexity creeps, you run the risk of it getting out of control.

Now that inout parameters are guaranteed exclusive, a mutating method on a struct or a function that takes inout parameters is isomorphic to one that consumes the initial value as a pure argument and returns the modified value back. This provides a value-semantics-friendly notion of purity, where a function can still be considered pure if the only thing it mutates is its unescaped local state and its inout parameters and it doesn't read or write any shared mutable state such as mutable globals, instance properties, or escaped variables. That gives you the ability to declare local variables and composably apply "pure" mutating operations to them inside a pure function. We've already brought Swift somewhat into the effects-system design space with "throws" (and "async", if it gets taken as we've currently proposed it), and we already have some abstraction debt to pay off with "throws"; if we wanted to, we could conceivably fold "impure" into that system as well. While it's true that there would be a lot of effort in propagating pure annotations to the right places, that's going to be true of any attempt to move the current everything-goes world into a more robust and constrained framework. I don't think we should write off the notion completely.

I agree with Joe here... in principle. We keep finding features we want to add that have effects-system-like semantics, and I expect that we will continue to do so. The best language design would probably result from addressing this category of feature in some holistic way. My main concern is that in practice, it seems unlikely to be doable before ABI lockdown makes it too late.

As Andy says, the major problem is that public API (which you can't see

the implementation of in general) would all need to be marked up properly.

The use-case I'm most interested in is reducing programmer error, I'm
personally not too concerned about optimisations achieved by "pure" at this
stage.

This means I'd be happy in the short-term to ignore a verified strong
purity requirement, and allow "pure" to be achieved in multiple
stages/releases. A gradual adoption would also make it easier to enforce
the concept strictly in future.

Purity could initial be defined in two levels, weak and strong.

   - *strong purity*: everything in the function has strong purity, what
   we've been discussing
   - *weak purity*: everything in the function is strong-pure, weak-pure,
   or says "trust me"

The following code suggest how this might work, it introduces a few things:

   - *pure* keyword, indicates that a function is has either weak or strong
   purity
   - *pretendPure* (ignore the name) pretends whatever happens inside it is
   pure, it has weak purity

func strongPurityFunction(_ lhs: Int, rhs: Int) pure -> Int {

    return lhs + rhs

}

func weakPurityFunction(value: Int) pure -> Int {

    return pretendPure {

        functionFromAnotherLibrary(value)

    }

}

func anotherWeakPurityFunction(value: Int) pure -> Int {

    return weakPurityFunction(value: value)

}

When this gets compiled to SIL the pure annotations would specialised as:

func strongPurityFunction(_ lhs: Int, rhs: Int) _strong_pure -> Int
func weakPurityFunction(value: Int) _weak_pure -> Int

func anotherWeakPurityFunction(value: Int) _weak_pure -> Int

Marking a function as pure, but using an unannotated function could start
as a warning, then become an error.

func failingFunction(value: Int) pure -> Int {

    return nonPureFunction(value: value)

}

The staged rollout could go something like this:

   - _weak_pure only with *pretendPure* or equivalent
   - _strong_pure, _weak_pure, and *pretendPure*
   - _strong_pure, deprecate _weak_pure, and *pretendPure*
   - _strong_pure only

···

On Sun, Sep 10, 2017 at 3:01 PM, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

> On Sep 9, 2017, at 10:48 AM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>
> on Wed Aug 23 2017, Joe Groff <swift-evolution@swift.org> wrote:
>>>> On Aug 18, 2017, at 12:10 PM, Chris Lattner via swift-evolution <
swift-evolution@swift.org> wrote: Splitting this out from the
concurrency thread:
>>>>
>>>>> On Aug 18, 2017, at 6:12 AM, Matthew Johnson <matthew@anandabits.com> > wrote:
>>>>>> On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner@nondot.org> > wrote:
>>>>>> In the manifesto you talk about restrictions on passing functions
across an actor message. You didn’t discuss pure functions, presumably
because Swift doesn’t have them yet. I imagine that if (hopefully when)
Swift has compiler support for verifying pure functions these would also be
safe to pass across an actor message. Is that correct?
>>>>> Correct. The proposal is specifically/intentionally designed to be
light on type system additions, but there are many that could make it
better in various ways. The logic for this approach is that I expect *a
lot* of people will be writing mostly straight-forward concurrent code, and
that goal is harmed by presenting significant type system hurdles for them
to jump over, because that implies a higher learning curve. This is why
the proposal doesn’t focus on a provably memory safe system: If someone
slaps “ValueSemantical” on a type that doesn’t obey, they will break the
invariants of the system. There are lots of ways to solve that problem
(e.g. the capabilities system in Pony) but it introduces a steep learning
curve. I haven’t thought a lot about practically getting pure functions
into Swift, because it wasn’t clear what problems it would solve (which
couldn’t be solved another way). You’re right though that this could be an
interesting motivator.
>>>> I can provide a concrete example of why this is definitely and
important motivator. My current project uses pure functions, value
semantics and declarative effects at the application level and moves as
much of the imperative code as possible (including effect handling) into
library level code. This is working out really well and I plan to continue
with this approach. The library level code needs the ability to schedule
user code in the appropriate context. There will likely be some
declarative ability for application level code to influence the context,
priority, etc, but it is the library that will be moving the functions to
the final context. They are obviously not closure literals from the
perspective of the library. Pure functions are obviously important to the
semantics of this approach. We can get by without compiler verification,
using documentation just as we do for protocol requirements that can't be
verified. That said, it would be pretty disappointing to have to avoid
using actors in the implementation simply because we can't move pure
functions from one actor to another as necessary. To be clear, I am
talking in the context of "the fullness of time". It would be perfectly
acceptable to ship actors before pure functions. That said, I do think it's
crucial that we eventually have the ability to verify pure functions and
move them around at will.
>>> Right. Pure functions are also nice when you care about thread
safety, and there is a lot of work on this. C has __attribute__((const))
and ((pure)) for example, c++ has constexpr, and many research languages
have built full blown effects systems. My principle concern is that
things like this quickly become infectious: LOTS of things are pure
functions, and requiring them all to be marked as such becomes a lot of
boilerplate and conceptual overhead. This is happening in the C++
community with constexpr for example. The secondary concern is that you
need to build out the model enough that you don’t prevent abstractions. A
pure function should be able to create an instance of a struct, mutate it
(i.e. calling non-pure functions) etc. This requires a non-trivial design,
and as the design complexity creeps, you run the risk of it getting out of
control.
>> Now that inout parameters are guaranteed exclusive, a mutating method
on a struct or a function that takes inout parameters is isomorphic to one
that consumes the initial value as a pure argument and returns the modified
value back. This provides a value-semantics-friendly notion of purity,
where a function can still be considered pure if the only thing it mutates
is its unescaped local state and its inout parameters and it doesn't read
or write any shared mutable state such as mutable globals, instance
properties, or escaped variables. That gives you the ability to declare
local variables and composably apply "pure" mutating operations to them
inside a pure function. We've already brought Swift somewhat into the
effects-system design space with "throws" (and "async", if it gets taken as
we've currently proposed it), and we already have some abstraction debt to
pay off with "throws"; if we wanted to, we could conceivably fold "impure"
into that system as well. While it's true that there would be a lot of
effort in propagating pure annotations to the right places, that's going to
be true of any attempt to move the current everything-goes world into a
more robust and constrained framework. I don't think we should write off
the notion completely.
>
> I agree with Joe here... in principle. We keep finding features we want
to add that have effects-system-like semantics, and I expect that we will
continue to do so. The best language design would probably result from
addressing this category of feature in some holistic way. My main concern
is that in practice, it seems unlikely to be doable before ABI lockdown
makes it too late.

Is there anything in particular that would make it more likely to be
doable before ABI lockdown? (Other that pushing it back again, I mean)

I'd guess that if it were as simple as adding a few "reserved for future
use" bits in the on-disk representation, you wouldn't have raised the issue.

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