[Proposal idea] Support for pure functions


(Jimmy Sambuo) #1

Hi Swift-Evolution,

I've been playing around with an idea, and I wanted to see what you think
about it.

My proposal is to add a `pure` keyword/attribute to Swift.

Similar to throws, you would mark a function as pure to say it will not
cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some
properties:

   1. The function must have a return value
   2. This function can only call other pure functions
   3. This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call
impure function 'date'

}

This would be similar to the pure keyword in D (
https://dlang.org/spec/function.html#pure-functions) or the noSideEffect
pragma in Nim (
http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a
"Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all
of my functions within the functional core as pure, the compiler can help
me if I accidentally start writing impure functions. In theory, this should
make my application simpler and more testable. Reviewing pull requests will
also be simpler since in the functional portion of my codebase, checking
for pure can be a criteria. Ideally, I'd run a static analyzer to see that
I have more pure functions than impure ones, which should help me control
complexity by encouraging me to have a larger "value" layer and smaller
"object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return "Implicitly
Impure Values" (similar to how every object from objective-c is an
implicitly unwrapped optional). All existing Swift functions are actually
returning a IO<SomeType>, and functions using that are implicitly
unwrapping them. Swift can be super nice by hiding this fact, making the
language much more familiar and accessible to developers not used to
purity. Adding `pure` allows devs to tap into the compiler's power and
prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

   - Explicit intentions - This allows design decisions to be communicated
   clearly to other developers and maintainers, as well as the compiler and
   other static analysis tools.
   - Compile-time guarantee - The compiler can help prevent unintentional
   state modifications.
   - Encouragement of better state management practices - More people will
   be aware of the concept of functional purity and may try to design their
   code to have more pure functions, which should make more code simpler and
   testable. This isn't a guarantee it will happen, but more people should
   understand it if it brings concrete results instead of just being an
   abstract concept.
   - Reduced cognitive load when understanding code - More pure functions
   should make it easier to reason about code.
   - Opt-in to purity - Swift will be just as accessible to new developers,
   but experienced developers will be able to take advantage of this.
   - Backwards compatible (mostly) - Existing codebases should still
   compile without any change (unless pure was used as a variable/method/class
   name).

Of course, there are risks of this feature. Some of the risks include:

   - This could make the language more difficult to work with. Developers
   maintaining an existing codebase with lots of pure function may become
   confused or frustrated when they realize they cannot do easy things such as
   logging within the method.
      - A counterargument to this may be that when Swift was introduced,
      optionals also made the language difficult to use. People implicitly
      unwrapped them just so things will compile, or returned them from methods
      without much consideration to what that implies. Nevertheless, this made
      Swift a safer language to use by explicitly when nil is a possible value.
      Developers are more conscious about nil being a potential value.
Similarly,
      I think developers should be more aware about side-effect causing
      functions. Being clear about this and separating these concerns should
      bring about more safer and testable Swift applications.
   - Implementation of this feature could be difficult. Swift can implement
   it in several different ways depending on the desired result of this
   feature, such as having stronger or weaker purity guarantees to make the
   feature easier to use or simpler to implement. An effect system may have to
   be created.
      - Alternatively, this could be done in a lightweight manner where the
      feature is introduced and known "pure-like" functions are marked
      in swift-corelibs-foundation.

To be honest, I don't have much experience in languages that have this
feature. I would imagine many people are interested in the notion of pure
functions, but the question here is if it would be worth it, feasible, or
even aligned with Swift's goals to have this feature. I'm half-expecting
that this has already been brought up and determined to be out of scope or
not a good idea.

Thanks for your consideration. Please let me know what you think.

···

--
Jimmy Sambuo
www.sambuo.com


(Andrew Bennett) #2

I have wanted to propose this myself. The main advantage I want from it is
that you could have a @pure on closure type signatures, similar to
@noescape to restrict the type of closures that can be used.

···

On Sun, Dec 20, 2015 at 4:00 PM, Jimmy Sambuo via swift-evolution < swift-evolution@swift.org> wrote:

Hi Swift-Evolution,

I've been playing around with an idea, and I wanted to see what you think
about it.

My proposal is to add a `pure` keyword/attribute to Swift.

Similar to throws, you would mark a function as pure to say it will not
cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some
properties:

   1. The function must have a return value
   2. This function can only call other pure functions
   3. This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call
impure function 'date'

}

This would be similar to the pure keyword in D (
https://dlang.org/spec/function.html#pure-functions) or the noSideEffect
pragma in Nim (
http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a
"Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all
of my functions within the functional core as pure, the compiler can help
me if I accidentally start writing impure functions. In theory, this should
make my application simpler and more testable. Reviewing pull requests will
also be simpler since in the functional portion of my codebase, checking
for pure can be a criteria. Ideally, I'd run a static analyzer to see that
I have more pure functions than impure ones, which should help me control
complexity by encouraging me to have a larger "value" layer and smaller
"object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return
"Implicitly Impure Values" (similar to how every object from objective-c is
an implicitly unwrapped optional). All existing Swift functions are
actually returning a IO<SomeType>, and functions using that are implicitly
unwrapping them. Swift can be super nice by hiding this fact, making the
language much more familiar and accessible to developers not used to
purity. Adding `pure` allows devs to tap into the compiler's power and
prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

   - Explicit intentions - This allows design decisions to be
   communicated clearly to other developers and maintainers, as well as the
   compiler and other static analysis tools.
   - Compile-time guarantee - The compiler can help prevent unintentional
   state modifications.
   - Encouragement of better state management practices - More people
   will be aware of the concept of functional purity and may try to design
   their code to have more pure functions, which should make more code simpler
   and testable. This isn't a guarantee it will happen, but more people should
   understand it if it brings concrete results instead of just being an
   abstract concept.
   - Reduced cognitive load when understanding code - More pure functions
   should make it easier to reason about code.
   - Opt-in to purity - Swift will be just as accessible to new
   developers, but experienced developers will be able to take advantage of
   this.
   - Backwards compatible (mostly) - Existing codebases should still
   compile without any change (unless pure was used as a variable/method/class
   name).

Of course, there are risks of this feature. Some of the risks include:

   - This could make the language more difficult to work with. Developers
   maintaining an existing codebase with lots of pure function may become
   confused or frustrated when they realize they cannot do easy things such as
   logging within the method.
      - A counterargument to this may be that when Swift was introduced,
      optionals also made the language difficult to use. People implicitly
      unwrapped them just so things will compile, or returned them from methods
      without much consideration to what that implies. Nevertheless, this made
      Swift a safer language to use by explicitly when nil is a possible value.
      Developers are more conscious about nil being a potential value. Similarly,
      I think developers should be more aware about side-effect causing
      functions. Being clear about this and separating these concerns should
      bring about more safer and testable Swift applications.
   - Implementation of this feature could be difficult. Swift can
   implement it in several different ways depending on the desired result of
   this feature, such as having stronger or weaker purity guarantees to make
   the feature easier to use or simpler to implement. An effect system may
   have to be created.
      - Alternatively, this could be done in a lightweight manner where
      the feature is introduced and known "pure-like" functions are marked
      in swift-corelibs-foundation.

To be honest, I don't have much experience in languages that have this
feature. I would imagine many people are interested in the notion of pure
functions, but the question here is if it would be worth it, feasible, or
even aligned with Swift's goals to have this feature. I'm half-expecting
that this has already been brought up and determined to be out of scope or
not a good idea.

Thanks for your consideration. Please let me know what you think.
--
Jimmy Sambuo
www.sambuo.com

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


(Chris Lattner) #3

My proposal is to add a `pure` keyword/attribute to Swift.

I’m a fan of this in concept, and the mechanics for this feature would probably allow us to have safe “computed lets” as well. My one concern about this is that there probably has to be some way to unsafely “force a call to a non-pure function to be allowed in a pure one”, both because of type system limitations as well as interoperability with C and other languages. Even ignoring issues around errno, it would be sad for a pure function to not be able to call “sin(x)” just because it weren’t marked __attribute__((const)).

A proposal in this area should consider how “pure” would interact with the type system, and it would probably make sense as an attribute @pure instead of a declmodifier.

-Chris

···

On Dec 19, 2015, at 9:00 PM, Jimmy Sambuo via swift-evolution <swift-evolution@swift.org> wrote:

Similar to throws, you would mark a function as pure to say it will not cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some properties:

The function must have a return value
This function can only call other pure functions
This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call impure function 'date' 

}

This would be similar to the pure keyword in D (https://dlang.org/spec/function.html#pure-functions) or the noSideEffect pragma in Nim (http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a "Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all of my functions within the functional core as pure, the compiler can help me if I accidentally start writing impure functions. In theory, this should make my application simpler and more testable. Reviewing pull requests will also be simpler since in the functional portion of my codebase, checking for pure can be a criteria. Ideally, I'd run a static analyzer to see that I have more pure functions than impure ones, which should help me control complexity by encouraging me to have a larger "value" layer and smaller "object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return "Implicitly Impure Values" (similar to how every object from objective-c is an implicitly unwrapped optional). All existing Swift functions are actually returning a IO<SomeType>, and functions using that are implicitly unwrapping them. Swift can be super nice by hiding this fact, making the language much more familiar and accessible to developers not used to purity. Adding `pure` allows devs to tap into the compiler's power and prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

Explicit intentions - This allows design decisions to be communicated clearly to other developers and maintainers, as well as the compiler and other static analysis tools.
Compile-time guarantee - The compiler can help prevent unintentional state modifications.
Encouragement of better state management practices - More people will be aware of the concept of functional purity and may try to design their code to have more pure functions, which should make more code simpler and testable. This isn't a guarantee it will happen, but more people should understand it if it brings concrete results instead of just being an abstract concept.
Reduced cognitive load when understanding code - More pure functions should make it easier to reason about code.
Opt-in to purity - Swift will be just as accessible to new developers, but experienced developers will be able to take advantage of this.
Backwards compatible (mostly) - Existing codebases should still compile without any change (unless pure was used as a variable/method/class name).
Of course, there are risks of this feature. Some of the risks include:

This could make the language more difficult to work with. Developers maintaining an existing codebase with lots of pure function may become confused or frustrated when they realize they cannot do easy things such as logging within the method.
A counterargument to this may be that when Swift was introduced, optionals also made the language difficult to use. People implicitly unwrapped them just so things will compile, or returned them from methods without much consideration to what that implies. Nevertheless, this made Swift a safer language to use by explicitly when nil is a possible value. Developers are more conscious about nil being a potential value. Similarly, I think developers should be more aware about side-effect causing functions. Being clear about this and separating these concerns should bring about more safer and testable Swift applications.
Implementation of this feature could be difficult. Swift can implement it in several different ways depending on the desired result of this feature, such as having stronger or weaker purity guarantees to make the feature easier to use or simpler to implement. An effect system may have to be created.
Alternatively, this could be done in a lightweight manner where the feature is introduced and known "pure-like" functions are marked in swift-corelibs-foundation.
To be honest, I don't have much experience in languages that have this feature. I would imagine many people are interested in the notion of pure functions, but the question here is if it would be worth it, feasible, or even aligned with Swift's goals to have this feature. I'm half-expecting that this has already been brought up and determined to be out of scope or not a good idea.

Thanks for your consideration. Please let me know what you think.

--
Jimmy Sambuo
www.sambuo.com <http://www.sambuo.com/> _______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rudolf Adamkovič) #4

+1

In my framework, I have a set of closures with memoized inputs. These are supplied by the user. Right now, all I can do is to document that all supplied closures need to be 100% pure.

R+

···

On 20 Dec 2015, at 06:00, Jimmy Sambuo via swift-evolution <swift-evolution@swift.org> wrote:

Hi Swift-Evolution,

I've been playing around with an idea, and I wanted to see what you think about it.

My proposal is to add a `pure` keyword/attribute to Swift.

Similar to throws, you would mark a function as pure to say it will not cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some properties:

The function must have a return value
This function can only call other pure functions
This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call impure function 'date' 

}

This would be similar to the pure keyword in D (https://dlang.org/spec/function.html#pure-functions) or the noSideEffect pragma in Nim (http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a "Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all of my functions within the functional core as pure, the compiler can help me if I accidentally start writing impure functions. In theory, this should make my application simpler and more testable. Reviewing pull requests will also be simpler since in the functional portion of my codebase, checking for pure can be a criteria. Ideally, I'd run a static analyzer to see that I have more pure functions than impure ones, which should help me control complexity by encouraging me to have a larger "value" layer and smaller "object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return "Implicitly Impure Values" (similar to how every object from objective-c is an implicitly unwrapped optional). All existing Swift functions are actually returning a IO<SomeType>, and functions using that are implicitly unwrapping them. Swift can be super nice by hiding this fact, making the language much more familiar and accessible to developers not used to purity. Adding `pure` allows devs to tap into the compiler's power and prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

Explicit intentions - This allows design decisions to be communicated clearly to other developers and maintainers, as well as the compiler and other static analysis tools.
Compile-time guarantee - The compiler can help prevent unintentional state modifications.
Encouragement of better state management practices - More people will be aware of the concept of functional purity and may try to design their code to have more pure functions, which should make more code simpler and testable. This isn't a guarantee it will happen, but more people should understand it if it brings concrete results instead of just being an abstract concept.
Reduced cognitive load when understanding code - More pure functions should make it easier to reason about code.
Opt-in to purity - Swift will be just as accessible to new developers, but experienced developers will be able to take advantage of this.
Backwards compatible (mostly) - Existing codebases should still compile without any change (unless pure was used as a variable/method/class name).
Of course, there are risks of this feature. Some of the risks include:

This could make the language more difficult to work with. Developers maintaining an existing codebase with lots of pure function may become confused or frustrated when they realize they cannot do easy things such as logging within the method.
A counterargument to this may be that when Swift was introduced, optionals also made the language difficult to use. People implicitly unwrapped them just so things will compile, or returned them from methods without much consideration to what that implies. Nevertheless, this made Swift a safer language to use by explicitly when nil is a possible value. Developers are more conscious about nil being a potential value. Similarly, I think developers should be more aware about side-effect causing functions. Being clear about this and separating these concerns should bring about more safer and testable Swift applications.
Implementation of this feature could be difficult. Swift can implement it in several different ways depending on the desired result of this feature, such as having stronger or weaker purity guarantees to make the feature easier to use or simpler to implement. An effect system may have to be created.
Alternatively, this could be done in a lightweight manner where the feature is introduced and known "pure-like" functions are marked in swift-corelibs-foundation.
To be honest, I don't have much experience in languages that have this feature. I would imagine many people are interested in the notion of pure functions, but the question here is if it would be worth it, feasible, or even aligned with Swift's goals to have this feature. I'm half-expecting that this has already been brought up and determined to be out of scope or not a good idea.

Thanks for your consideration. Please let me know what you think.

--
Jimmy Sambuo
www.sambuo.com <http://www.sambuo.com/> _______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jacob Bandes-Storch) #5

Minor tangent, but should the same apply to @noescape? (Lack of this is why
I bothered with SE-0012
<https://github.com/apple/swift-evolution/blob/master/proposals/0012-add-noescape-to-public-library-api.md>
)

Jacob

···

On Mon, Dec 21, 2015 at 11:55 AM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

…there probably has to be some way to unsafely “force a call to a non-pure
function to be allowed in a pure one”, both because of type system
limitations as well as interoperability with C and other languages. Even
ignoring issues around errno, it would be sad for a pure function to not be
able to call “sin(x)” just because it weren’t marked __attribute__((const)).


(TJ Usiyan) #6

I have been thinking about this proposal since the list opened up. I think
that @read(none|only) should be considered as an alternative. Just as in
clang and the optimizer annotations @read(none) would be the attribute
described and @read(only) could read global variables but could not write.
Is it possible to generate a runtime error if we guarantee that function A
is pure, only to call it and 'find out' that it isn't? If we can, then do
try syntax could be considered.

···

On Mon, Dec 21, 2015 at 2:55 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 19, 2015, at 9:00 PM, Jimmy Sambuo via swift-evolution < > swift-evolution@swift.org> wrote:

My proposal is to add a `pure` keyword/attribute to Swift.

I’m a fan of this in concept, and the mechanics for this feature would
probably allow us to have safe “computed lets” as well. My one concern
about this is that there probably has to be some way to unsafely “force a
call to a non-pure function to be allowed in a pure one”, both because of
type system limitations as well as interoperability with C and other
languages. Even ignoring issues around errno, it would be sad for a pure
function to not be able to call “sin(x)” just because it weren’t marked
__attribute__((const)).

A proposal in this area should consider how “pure” would interact with the
type system, and it would probably make sense as an attribute @pure instead
of a declmodifier.

-Chris

Similar to throws, you would mark a function as pure to say it will not
cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some
properties:

   1. The function must have a return value
   2. This function can only call other pure functions
   3. This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call
impure function 'date'

}

This would be similar to the pure keyword in D (
https://dlang.org/spec/function.html#pure-functions) or the noSideEffect
pragma in Nim (
http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a
"Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all
of my functions within the functional core as pure, the compiler can help
me if I accidentally start writing impure functions. In theory, this should
make my application simpler and more testable. Reviewing pull requests will
also be simpler since in the functional portion of my codebase, checking
for pure can be a criteria. Ideally, I'd run a static analyzer to see that
I have more pure functions than impure ones, which should help me control
complexity by encouraging me to have a larger "value" layer and smaller
"object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return
"Implicitly Impure Values" (similar to how every object from objective-c is
an implicitly unwrapped optional). All existing Swift functions are
actually returning a IO<SomeType>, and functions using that are implicitly
unwrapping them. Swift can be super nice by hiding this fact, making the
language much more familiar and accessible to developers not used to
purity. Adding `pure` allows devs to tap into the compiler's power and
prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

   - Explicit intentions - This allows design decisions to be
   communicated clearly to other developers and maintainers, as well as the
   compiler and other static analysis tools.
   - Compile-time guarantee - The compiler can help prevent unintentional
   state modifications.
   - Encouragement of better state management practices - More people
   will be aware of the concept of functional purity and may try to design
   their code to have more pure functions, which should make more code simpler
   and testable. This isn't a guarantee it will happen, but more people should
   understand it if it brings concrete results instead of just being an
   abstract concept.
   - Reduced cognitive load when understanding code - More pure functions
   should make it easier to reason about code.
   - Opt-in to purity - Swift will be just as accessible to new
   developers, but experienced developers will be able to take advantage of
   this.
   - Backwards compatible (mostly) - Existing codebases should still
   compile without any change (unless pure was used as a variable/method/class
   name).

Of course, there are risks of this feature. Some of the risks include:

   - This could make the language more difficult to work with. Developers
   maintaining an existing codebase with lots of pure function may become
   confused or frustrated when they realize they cannot do easy things such as
   logging within the method.
      - A counterargument to this may be that when Swift was introduced,
      optionals also made the language difficult to use. People implicitly
      unwrapped them just so things will compile, or returned them from methods
      without much consideration to what that implies. Nevertheless, this made
      Swift a safer language to use by explicitly when nil is a possible value.
      Developers are more conscious about nil being a potential value. Similarly,
      I think developers should be more aware about side-effect causing
      functions. Being clear about this and separating these concerns should
      bring about more safer and testable Swift applications.
   - Implementation of this feature could be difficult. Swift can
   implement it in several different ways depending on the desired result of
   this feature, such as having stronger or weaker purity guarantees to make
   the feature easier to use or simpler to implement. An effect system may
   have to be created.
      - Alternatively, this could be done in a lightweight manner where
      the feature is introduced and known "pure-like" functions are marked
      in swift-corelibs-foundation.

To be honest, I don't have much experience in languages that have this
feature. I would imagine many people are interested in the notion of pure
functions, but the question here is if it would be worth it, feasible, or
even aligned with Swift's goals to have this feature. I'm half-expecting
that this has already been brought up and determined to be out of scope or
not a good idea.

Thanks for your consideration. Please let me know what you think.
--
Jimmy Sambuo
www.sambuo.com
_______________________________________________
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


(Joe Groff) #7

Another approach here is an effects system. If a pure function were spelled `(T) => U`, then `readonly` could be expressed as `(T) reads => U`, and (T) -> U could remain as a synonym for (T) reads, writes => U, in the spirit of encouraging immutability without punishing mutability.

-Joe

···

On Dec 21, 2015, at 12:20 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I have been thinking about this proposal since the list opened up. I think that @read(none|only) should be considered as an alternative. Just as in clang and the optimizer annotations @read(none) would be the attribute described and @read(only) could read global variables but could not write. Is it possible to generate a runtime error if we guarantee that function A is pure, only to call it and 'find out' that it isn't? If we can, then do try syntax could be considered.


(Chris Lattner) #8

Yes, it should. I believe you can currently use an unsafe cast to remove @noescape, and that the stdlib does it in a few places. Dmitri, do you know where?

-Chris

···

On Dec 21, 2015, at 12:05 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

On Mon, Dec 21, 2015 at 11:55 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
…there probably has to be some way to unsafely “force a call to a non-pure function to be allowed in a pure one”, both because of type system limitations as well as interoperability with C and other languages. Even ignoring issues around errno, it would be sad for a pure function to not be able to call “sin(x)” just because it weren’t marked __attribute__((const)).

Minor tangent, but should the same apply to @noescape?


(Chris Lattner) #9

I have been thinking about this proposal since the list opened up. I think that @read(none|only) should be considered as an alternative. Just as in clang and the optimizer annotations @read(none) would be the attribute described and @read(only) could read global variables but could not write. Is it possible to generate a runtime error if we guarantee that function A is pure, only to call it and 'find out' that it isn't? If we can, then do try syntax could be considered.

Yes, this is worth considering, but it would be nice to avoid this if possible. There are two interesting related-but-different problems that could be tackled here:

1. Optimization hints: These would align to llvm’s readnone/readonly attributes (const/pure in GCC nomenclature).
2. Expanding the existing ‘let’ language model from properties to functions: this would allow computed lets.

I’m personally more interested in making #2 happen. It is the attribute that (like noescape) would need to permeate the type system.

#1 could be interesting, but it could also be a different thing that is an attribute of a declaration, not an attribute of the declaration’s type. LLVM also supports a number of other interesting attributes that can be useful to expose someday in Swift, but I’m not keen to do that unless there is a very strong and specific need to do so:
http://llvm.org/docs/LangRef.html#id671

-Chris

···

On Dec 21, 2015, at 12:20 PM, T.J. Usiyan <griotspeak@gmail.com> wrote:

On Mon, Dec 21, 2015 at 2:55 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Dec 19, 2015, at 9:00 PM, Jimmy Sambuo via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My proposal is to add a `pure` keyword/attribute to Swift.

I’m a fan of this in concept, and the mechanics for this feature would probably allow us to have safe “computed lets” as well. My one concern about this is that there probably has to be some way to unsafely “force a call to a non-pure function to be allowed in a pure one”, both because of type system limitations as well as interoperability with C and other languages. Even ignoring issues around errno, it would be sad for a pure function to not be able to call “sin(x)” just because it weren’t marked __attribute__((const)).

A proposal in this area should consider how “pure” would interact with the type system, and it would probably make sense as an attribute @pure instead of a declmodifier.

-Chris

Similar to throws, you would mark a function as pure to say it will not cause any observable side-effects and hold referential transparency:


func pure add(x: Int, y: Int) -> Int {

    return x + y

}

By adding this attribute, the function is guaranteed to have some properties:

The function must have a return value
This function can only call other pure functions
This function cannot access/modify global or static variables.


func pure getDate() -> NSDate {

    return NSDate.date() // Error: pure function 'getDate' cannot call impure function 'date' 

}

This would be similar to the pure keyword in D (https://dlang.org/spec/function.html#pure-functions) or the noSideEffect pragma in Nim (http://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma).

My motivation for this is that I want to create applications that follow a "Functional Core, Imperative Shell" style [Gary Bernhardt]. By marking all of my functions within the functional core as pure, the compiler can help me if I accidentally start writing impure functions. In theory, this should make my application simpler and more testable. Reviewing pull requests will also be simpler since in the functional portion of my codebase, checking for pure can be a criteria. Ideally, I'd run a static analyzer to see that I have more pure functions than impure ones, which should help me control complexity by encouraging me to have a larger "value" layer and smaller "object" layer [Andy Matuschak].

Logically, I think of this as Swift having all functions return "Implicitly Impure Values" (similar to how every object from objective-c is an implicitly unwrapped optional). All existing Swift functions are actually returning a IO<SomeType>, and functions using that are implicitly unwrapping them. Swift can be super nice by hiding this fact, making the language much more familiar and accessible to developers not used to purity. Adding `pure` allows devs to tap into the compiler's power and prevent unwanted side-effects without directly exposing the IO type.

The benefits I see are:

Explicit intentions - This allows design decisions to be communicated clearly to other developers and maintainers, as well as the compiler and other static analysis tools.
Compile-time guarantee - The compiler can help prevent unintentional state modifications.
Encouragement of better state management practices - More people will be aware of the concept of functional purity and may try to design their code to have more pure functions, which should make more code simpler and testable. This isn't a guarantee it will happen, but more people should understand it if it brings concrete results instead of just being an abstract concept.
Reduced cognitive load when understanding code - More pure functions should make it easier to reason about code.
Opt-in to purity - Swift will be just as accessible to new developers, but experienced developers will be able to take advantage of this.
Backwards compatible (mostly) - Existing codebases should still compile without any change (unless pure was used as a variable/method/class name).
Of course, there are risks of this feature. Some of the risks include:

This could make the language more difficult to work with. Developers maintaining an existing codebase with lots of pure function may become confused or frustrated when they realize they cannot do easy things such as logging within the method.
A counterargument to this may be that when Swift was introduced, optionals also made the language difficult to use. People implicitly unwrapped them just so things will compile, or returned them from methods without much consideration to what that implies. Nevertheless, this made Swift a safer language to use by explicitly when nil is a possible value. Developers are more conscious about nil being a potential value. Similarly, I think developers should be more aware about side-effect causing functions. Being clear about this and separating these concerns should bring about more safer and testable Swift applications.
Implementation of this feature could be difficult. Swift can implement it in several different ways depending on the desired result of this feature, such as having stronger or weaker purity guarantees to make the feature easier to use or simpler to implement. An effect system may have to be created.
Alternatively, this could be done in a lightweight manner where the feature is introduced and known "pure-like" functions are marked in swift-corelibs-foundation.
To be honest, I don't have much experience in languages that have this feature. I would imagine many people are interested in the notion of pure functions, but the question here is if it would be worth it, feasible, or even aligned with Swift's goals to have this feature. I'm half-expecting that this has already been brought up and determined to be out of scope or not a good idea.

Thanks for your consideration. Please let me know what you think.

--
Jimmy Sambuo
www.sambuo.com <http://www.sambuo.com/> _______________________________________________
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


(Howard Lovatt) #10

-1 for using -> and => to mean different types of functions, not-pure and pure respectively. In fact I dislike using symbols unless they are really well known. Keywords read a lot better.

···

Sent from my iPad

On 22 Dec 2015, at 8:37 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 21, 2015, at 12:20 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I have been thinking about this proposal since the list opened up. I think that @read(none|only) should be considered as an alternative. Just as in clang and the optimizer annotations @read(none) would be the attribute described and @read(only) could read global variables but could not write. Is it possible to generate a runtime error if we guarantee that function A is pure, only to call it and 'find out' that it isn't? If we can, then do try syntax could be considered.

Another approach here is an effects system. If a pure function were spelled `(T) => U`, then `readonly` could be expressed as `(T) reads => U`, and (T) -> U could remain as a synonym for (T) reads, writes => U, in the spirit of encouraging immutability without punishing mutability.

-Joe

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


(Joe Groff) #11

The runtime does this using unsafeBitCast, but don't follow its example—I don't want to promise this will always be possible. There are representation optimizations we can do with @noescape closures that would be blocked if they always had to be bitcastable to refcounted escapable closures. I'd like to introduce a `Builtin.makeEscapable` operation specifically to go from @noescape to escapable, introducing a refcounting shim if necessary in the future.

-Joe

···

On Dec 21, 2015, at 1:49 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 21, 2015, at 12:05 PM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

On Mon, Dec 21, 2015 at 11:55 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
…there probably has to be some way to unsafely “force a call to a non-pure function to be allowed in a pure one”, both because of type system limitations as well as interoperability with C and other languages. Even ignoring issues around errno, it would be sad for a pure function to not be able to call “sin(x)” just because it weren’t marked __attribute__((const)).

Minor tangent, but should the same apply to @noescape?

Yes, it should. I believe you can currently use an unsafe cast to remove @noescape, and that the stdlib does it in a few places. Dmitri, do you know where?


(Alex Popov) #12

Slight tangent, would a guarantee of purity also allow for more Tail-Call
Optimizations? A cursory glance at SO seems to point to TCO not always being
applied, especially when ARC is involved.

<br

···

Alex Popov Jr.

Principal iOS Developer | Shelfie

On Dec 21 2015, at 1:55 pm, Joe Groff via swift-evolution &lt;swift- evolution@swift.org&gt; wrote:
  

On Dec 21, 2015, at 1:49 PM, Chris Lattner via swift-evolution &lt;[swift- evolution@swift.org](mailto:swift-evolution@swift.org)&gt; wrote:

On Dec 21, 2015, at 12:05 PM, Jacob Bandes-Storch &lt;[jtbandes@gmail.com](mailto:jtbandes@gmail.com)&gt; wrote:

On Mon, Dec 21, 2015 at 11:55 AM, Chris Lattner via swift-evolution &lt ;[swift-evolution@swift.org](mailto:swift-evolution@swift.org)&gt; wrote:

…there probably has to be some way to unsafely “force a call to a non-

pure function to be allowed in a pure one”, both because of type system
limitations as well as interoperability with C and other languages. Even
ignoring issues around errno, it would be sad for a pure function to not be
able to call “sin(x)” just because it weren’t marked __attribute__((const)).

Minor tangent, but should the same apply to @noescape?

Yes, it should. I believe you can currently use an unsafe cast to remove

@noescape, and that the stdlib does it in a few places. Dmitri, do you know
where?

The runtime does this using unsafeBitCast, but don't follow its example—I

don't want to promise this will always be possible. There are representation
optimizations we can do with @noescape closures that would be blocked if they
always had to be bitcastable to refcounted escapable closures. I'd like to
introduce a `Builtin.makeEscapable` operation specifically to go from
@noescape to escapable, introducing a refcounting shim if necessary in the
future.

-Joe

![](https://u2002410.ct.sendgrid.net/wf/open?upn=CmwAv3oRa0AH4Hd1bWC6X-

2BzbhPqo1YEo6mPHEujr90vNqvSlNKW0iy2BTd4OxR0SCJAwyLx2cZ3twpk4M4WqgQG-
2FHUcfU2eLopRmdzTmpLuLZtOY1eD9WFERKSEU-2Fh8ZpSXZO8n-2FxFdugKSpD2tVIHo-
2B1Xm1Kx9Z6REnSigwhXQc1ZZ21GoUrVYCyW9mvlG9yTXA-2F8
-2BkRL3jMogyILA8zEwqIWg15KQWC24Bm6WqsLc-3D)


(Joe Groff) #13

I don't think any reasonable meaning for `pure` in Swift would affect the possibility of TCO. There was another thread about TCO here you might read back on; as I explained there, ARC is not a barrier to TCO, our ownership and machine-level calling conventions are. We would need to be able to use a specific calling convention for guaranteed-TCOable entry points.

-Joe

···

On Dec 21, 2015, at 2:04 PM, Alex Popov via swift-evolution <swift-evolution@swift.org> wrote:

Slight tangent, would a guarantee of purity also allow for more Tail-Call Optimizations? A cursory glance at SO seems to point to TCO not always being applied, especially when ARC is involved.


(Andrew Bennett) #14

Hi,

I started another thread on @pure in swift, luckily Chris Lattner reminded
me about this one. I'm going to continue any discussion here so we don't
fragment the conversation.

The other thread was a pre-proposal discussion called "Proposal proposal:
@pure keyword", it isn't archived so I cannot link it.

I've summarised the progress so far here (it's in the proposals directory
for convenience):

https://github.com/therealbnut/swift/blob/therealbnut-pure-preproprosal/docs/proposals/PureKeyword.rst

If I've missed anything or you want to update, clarify, fix typos, etc.
please submit a PR :slight_smile: I'm trying to keep it focused on things that have
little contention.

I've tried to unify the ideas from both the other thread and this one into
that summary. As it's not really a proposal I haven't included the
excellent justifications that Jimmy initially stated, they can be added if
it becomes a proposal. Please add a PR if you would like them there.

···

On Tue, Dec 22, 2015 at 9:08 AM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

> On Dec 21, 2015, at 2:04 PM, Alex Popov via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Slight tangent, would a guarantee of purity also allow for more
Tail-Call Optimizations? A cursory glance at SO seems to point to TCO not
always being applied, especially when ARC is involved.

I don't think any reasonable meaning for `pure` in Swift would affect the
possibility of TCO. There was another thread about TCO here you might read
back on; as I explained there, ARC is not a barrier to TCO, our ownership
and machine-level calling conventions are. We would need to be able to use
a specific calling convention for guaranteed-TCOable entry points.

-Joe

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


(Jimmy Sambuo) #15

Thanks Andrew and everyone for your ideas and inputs.

Andrew, your summary is amazing. It covers so many non-trivial cases of
this feature.
I believe this is the archive of the other thread:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006005.html

I was hesitant to add more to this discussion because I was only able to
think of the trivial case, and I know this can have lots of hidden
complexities and implications. Looking at Andrew's summary gave me some
ideas for talking points. Specifically, the section with inout parameters
and pure mutating functions seems strange to me as I wasn't expecting
mutation to occur, but this may be a valid scenario for "weakly pure"
functions, where parameters can be modified.

Chris had mentioned that there probably should be some way to to unsafely
"force a call to a non-pure function to be allowed in a pure one," and that it
would probably make sense as an attribute @pure instead of a declmodifier.
Perhaps this is another form of "weakly pure" functions. I'm thinking in
this case, pure is acting as a marker, the type system does not have to
know about actual purity and an effects system would not have to be
implemented in this case. This should make implementation simpler and less
invasive to the existing type system.

Imagine if ! was used to call an impure function within a pure function:

/// Wrapper around C sin()
func sin(x: Double) @pure -> Double {
    return sin(x)!
}

In my mind, seeing ! tells me something could be wrong there so pay closer
attention. I would think `sin` is a C function so Swift doesn't know it
should be pure, but sine is a math function so it should be safe. What
about for actually impure functions? Optionals and try! crashes when
expectations are not met. Should `potentiallyImpureFunc()!` or
`mostlyPureFunc()!` crash if an "impure" value or side effect occurs? How
would the runtime know this if purity is defined by annotations? Maybe it
shouldn't crash, but now ! is slightly different from optionals and try!.
Perhaps there should be a different symbol for "unwrapping" impure
functions. Then there would be two different symbols for unwrapping.

My major concern is I'd want to avoid a situation where developers are
adding @pure and unwrapping impure functions just to make their app
compile, making @pure meaningless since most of their pure functions will
actually be impure. I'm not sure of a good way to prevent this.

···

On Sun, Jan 10, 2016 at 12:41 AM, Andrew Bennett via swift-evolution < swift-evolution@swift.org> wrote:

Hi,

I started another thread on @pure in swift, luckily Chris Lattner reminded
me about this one. I'm going to continue any discussion here so we don't
fragment the conversation.

The other thread was a pre-proposal discussion called "Proposal proposal:
@pure keyword", it isn't archived so I cannot link it.

I've summarised the progress so far here (it's in the proposals directory
for convenience):

https://github.com/therealbnut/swift/blob/therealbnut-pure-preproprosal/docs/proposals/PureKeyword.rst

If I've missed anything or you want to update, clarify, fix typos, etc.
please submit a PR :slight_smile: I'm trying to keep it focused on things that have
little contention.

I've tried to unify the ideas from both the other thread and this one into
that summary. As it's not really a proposal I haven't included the
excellent justifications that Jimmy initially stated, they can be added if
it becomes a proposal. Please add a PR if you would like them there.

On Tue, Dec 22, 2015 at 9:08 AM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

> On Dec 21, 2015, at 2:04 PM, Alex Popov via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> Slight tangent, would a guarantee of purity also allow for more
Tail-Call Optimizations? A cursory glance at SO seems to point to TCO not
always being applied, especially when ARC is involved.

I don't think any reasonable meaning for `pure` in Swift would affect the
possibility of TCO. There was another thread about TCO here you might read
back on; as I explained there, ARC is not a barrier to TCO, our ownership
and machine-level calling conventions are. We would need to be able to use
a specific calling convention for guaranteed-TCOable entry points.

-Joe

_______________________________________________
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

--
Jimmy Sambuo
www.sambuo.com


(Taras Zakharko) #16

I am very much in support to of a @pure decorator. It could unlock a new level of performance for Swift and make everything safer.

For the issue of essentially pure functions that cannot be statically validated as pure (due to calling external code etc, as described by many in this thread), I think that one way to solve it would be to use a decorator along the lines of @pure(unsafe). This decorator would mean that the compiler should treat this function as pure, ‘trusting’ the programmer.

- T.

···

On 10 Jan 2016, at 22:27, Jimmy Sambuo via swift-evolution <swift-evolution@swift.org> wrote:

Thanks Andrew and everyone for your ideas and inputs.

Andrew, your summary is amazing. It covers so many non-trivial cases of this feature.
I believe this is the archive of the other thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006005.html

I was hesitant to add more to this discussion because I was only able to think of the trivial case, and I know this can have lots of hidden complexities and implications. Looking at Andrew's summary gave me some ideas for talking points. Specifically, the section with inout parameters and pure mutating functions seems strange to me as I wasn't expecting mutation to occur, but this may be a valid scenario for "weakly pure" functions, where parameters can be modified.

Chris had mentioned that there probably should be some way to to unsafely "force a call to a non-pure function to be allowed in a pure one," and that it would probably make sense as an attribute @pure instead of a declmodifier. Perhaps this is another form of "weakly pure" functions. I'm thinking in this case, pure is acting as a marker, the type system does not have to know about actual purity and an effects system would not have to be implemented in this case. This should make implementation simpler and less invasive to the existing type system.

Imagine if ! was used to call an impure function within a pure function:

/// Wrapper around C sin()
func sin(x: Double) @pure -> Double {
    return sin(x)!
}

In my mind, seeing ! tells me something could be wrong there so pay closer attention. I would think `sin` is a C function so Swift doesn't know it should be pure, but sine is a math function so it should be safe. What about for actually impure functions? Optionals and try! crashes when expectations are not met. Should `potentiallyImpureFunc()!` or `mostlyPureFunc()!` crash if an "impure" value or side effect occurs? How would the runtime know this if purity is defined by annotations? Maybe it shouldn't crash, but now ! is slightly different from optionals and try!. Perhaps there should be a different symbol for "unwrapping" impure functions. Then there would be two different symbols for unwrapping.

My major concern is I'd want to avoid a situation where developers are adding @pure and unwrapping impure functions just to make their app compile, making @pure meaningless since most of their pure functions will actually be impure. I'm not sure of a good way to prevent this.

On Sun, Jan 10, 2016 at 12:41 AM, Andrew Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hi,

I started another thread on @pure in swift, luckily Chris Lattner reminded me about this one. I'm going to continue any discussion here so we don't fragment the conversation.

The other thread was a pre-proposal discussion called "Proposal proposal: @pure keyword", it isn't archived so I cannot link it.

I've summarised the progress so far here (it's in the proposals directory for convenience):

https://github.com/therealbnut/swift/blob/therealbnut-pure-preproprosal/docs/proposals/PureKeyword.rst

If I've missed anything or you want to update, clarify, fix typos, etc. please submit a PR :slight_smile: I'm trying to keep it focused on things that have little contention.

I've tried to unify the ideas from both the other thread and this one into that summary. As it's not really a proposal I haven't included the excellent justifications that Jimmy initially stated, they can be added if it becomes a proposal. Please add a PR if you would like them there.

On Tue, Dec 22, 2015 at 9:08 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Dec 21, 2015, at 2:04 PM, Alex Popov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Slight tangent, would a guarantee of purity also allow for more Tail-Call Optimizations? A cursory glance at SO seems to point to TCO not always being applied, especially when ARC is involved.

I don't think any reasonable meaning for `pure` in Swift would affect the possibility of TCO. There was another thread about TCO here you might read back on; as I explained there, ARC is not a barrier to TCO, our ownership and machine-level calling conventions are. We would need to be able to use a specific calling convention for guaranteed-TCOable entry points.

-Joe

_______________________________________________
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

--
Jimmy Sambuo
www.sambuo.com <http://www.sambuo.com/> _______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Andrew Bennett) #17

Hi Jimmy,

Interesting ideas with !, although I'm worried it would clash with other
uses of the operator. For example: optional return types. There's probably
more precedent for this syntax: sin!(x), but it will also clash in some
cases (optional function in objc protocol, optional closure).

I think something much more verbose (a function) would be fine as it's
probably not going to be called often. I expect initially it's probably
sufficient for it to be a Builtin function only used in the standard
library. Failing that a global clearly unsafe function:
unsafelyPurifyFunction(...).

With inout I don't think it is actually weaker, I could be wrong. I see the
following as equivalent (from a purity standpoint):

func test1(inout a: Int) @pure -> Double {}
x = test1(&a)

func test2(a: Int) @pure -> (Double, Int) {}
(x, a) = test2(a)

If they're equivalent then this can be pure:

extension Int {

mutating func addSome(x: Int) @pure {

self += x

}

}

let x: (inout Int, Int) @pure -> Void = Int.addOne

However the curried function signature must be adjusted so it cannot be
partially applied, previously it would have been this:

let x: (inout Int) -> (Int) -> Void = Int.addOne

If we're happy with this equivalence, I don't think it's a useful to
distinguish inout and other pure functions (no need for @pure(unsafe)
here). As long as the parameters predictably cover everything affected I am
happy.

Reference types may be an issue here, but no more than elsewhere.

···

----

As far as weak-pure goes: I think references have a lot of issues with
@pure. It may be necessary to know if any types a @pure function uses
interact at all with a reference type, stores it, uses it to compute, etc.

A gotcha example:

AnySequence uses a class internally (dynamic dispatch for type erasure), a
pure function may want to return AnySequence, which appears to be a value
type. See ExistentialCollection.swift.gyb.

The compiler may not be able to ensure two sequences with the same input
are equivalent because their internal class has a different reference.
Equitable could definitely work, but at compile time?

This is going to be a problem for referential transparency.

Possible Solutions
1) Reference types or types composed of reference types are initially
unavailable to @pure functions, unless locally scoped.
2) Relax the referential transparency requirement when the output is a
reference type or composed of reference types.
3) Add a @copy keyword for types, this would mean it can be safely use
copy-on-write with no shared references. I think this would be useful in
other cases too, but should be avoided if possible as it's a large change
that would require other proposal(s).

Not entirely a solution, but interesting:

You cannot return a newly allocated reference type, but you can return a
closure: () -> ReferenceType

It can still use memoization to always return the same closure, avoid any
computation to derive the closure, but the closure doesn't necessarily
return the same reference.

On Mon, Jan 11, 2016 at 8:27 AM, Jimmy Sambuo <jsambuo@gmail.com> wrote:

Thanks Andrew and everyone for your ideas and inputs.

Andrew, your summary is amazing. It covers so many non-trivial cases of
this feature.
I believe this is the archive of the other thread:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006005.html

I was hesitant to add more to this discussion because I was only able to
think of the trivial case, and I know this can have lots of hidden
complexities and implications. Looking at Andrew's summary gave me some
ideas for talking points. Specifically, the section with inout parameters
and pure mutating functions seems strange to me as I wasn't expecting
mutation to occur, but this may be a valid scenario for "weakly pure"
functions, where parameters can be modified.

Chris had mentioned that there probably should be some way to to unsafely
"force a call to a non-pure function to be allowed in a pure one," and that it
would probably make sense as an attribute @pure instead of a declmodifier.
Perhaps this is another form of "weakly pure" functions. I'm thinking in
this case, pure is acting as a marker, the type system does not have to
know about actual purity and an effects system would not have to be
implemented in this case. This should make implementation simpler and less
invasive to the existing type system.

Imagine if ! was used to call an impure function within a pure function:

/// Wrapper around C sin()
func sin(x: Double) @pure -> Double {
    return sin(x)!
}

In my mind, seeing ! tells me something could be wrong there so pay closer
attention. I would think `sin` is a C function so Swift doesn't know it
should be pure, but sine is a math function so it should be safe. What
about for actually impure functions? Optionals and try! crashes when
expectations are not met. Should `potentiallyImpureFunc()!` or
`mostlyPureFunc()!` crash if an "impure" value or side effect occurs? How
would the runtime know this if purity is defined by annotations? Maybe it
shouldn't crash, but now ! is slightly different from optionals and try!.
Perhaps there should be a different symbol for "unwrapping" impure
functions. Then there would be two different symbols for unwrapping.

My major concern is I'd want to avoid a situation where developers are
adding @pure and unwrapping impure functions just to make their app
compile, making @pure meaningless since most of their pure functions will
actually be impure. I'm not sure of a good way to prevent this.

On Sun, Jan 10, 2016 at 12:41 AM, Andrew Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

Hi,

I started another thread on @pure in swift, luckily Chris Lattner
reminded me about this one. I'm going to continue any discussion here so we
don't fragment the conversation.

The other thread was a pre-proposal discussion called "Proposal proposal:
@pure keyword", it isn't archived so I cannot link it.

I've summarised the progress so far here (it's in the proposals directory
for convenience):

https://github.com/therealbnut/swift/blob/therealbnut-pure-preproprosal/docs/proposals/PureKeyword.rst

If I've missed anything or you want to update, clarify, fix typos, etc.
please submit a PR :slight_smile: I'm trying to keep it focused on things that have
little contention.

I've tried to unify the ideas from both the other thread and this one
into that summary. As it's not really a proposal I haven't included the
excellent justifications that Jimmy initially stated, they can be added if
it becomes a proposal. Please add a PR if you would like them there.

On Tue, Dec 22, 2015 at 9:08 AM, Joe Groff via swift-evolution < >> swift-evolution@swift.org> wrote:

> On Dec 21, 2015, at 2:04 PM, Alex Popov via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
> Slight tangent, would a guarantee of purity also allow for more
Tail-Call Optimizations? A cursory glance at SO seems to point to TCO not
always being applied, especially when ARC is involved.

I don't think any reasonable meaning for `pure` in Swift would affect
the possibility of TCO. There was another thread about TCO here you might
read back on; as I explained there, ARC is not a barrier to TCO, our
ownership and machine-level calling conventions are. We would need to be
able to use a specific calling convention for guaranteed-TCOable entry
points.

-Joe

_______________________________________________
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

--
Jimmy Sambuo
www.sambuo.com