[Idea] Add @pure annotation or keyword to force pure functions


(Guido Marucci Blas) #1

Hi, I wanted to discussed an idea that I’ve been thinking for the last couple of weeks. First I want to give you a little bit of context. I am the technical leader in the iOS project I am working on. We have been using Swift for almost one year and half. Most of the first part of the project was desinged by me. I am being trying to apply concepts from the functional paradigm and in particular we heavily depend on ReactiveCocoa. For the last year two less experienced developers joined the team. I was in charge of teaching them about iOS programming (this was the first time doing an iOS app for them), functional programming (they are still college student with a background in OOP) and the details of our code base.

Coming from a background in OOP and imperative programming makes the change to a more functional approach quite “interesting”. I really like Swift and its flexibility to be able to tackle both low level and high level problems. But some times this flexibility makes it more error prone when you try to apply functional programming concepts. For example I found my self reviewing and correcting pull request with things like “When you use functions like map or filter on a collection or a SignalProducer don’t inject side effects”.

This led me to the idea of introducing a @pure annotation for functions. Full disclosure: I have almost no background in compilers and the internals of the Swift language so I have no idea if this makes sense at all. Such a annotation (it could also be a keyword like override o mutating) will make the compiler check if the function is actually a pure function, where by Pure I mean

  - It doesn’t mutate any internal or global state
  - It doesn’t call any non-pure function
  - It must have a return type != from Void
  - It can access non mutable state

In this context a pure function is “stronger” than the mutating keyword in the sense that the pure function won’t allow any type of mutation even if you are calling a non-pure method (which performs a mutation or a side effect) on an object (reference type) instead of a struct. For example

final class Foo {
    
    private var count = 0
    
    func foo(x: Int) -> Int {
        count += x
        return count
    }
    
}

struct Bar {
    
    var count: Int
    var foo: Foo
    
    // This method performs a mutation
    // even though is not marked as mutating
    // You could call it a "transitive mutation".
    func bar(x: Int) -> Int {
        return foo.foo(x)
    }
    
    mutating func baz(x: Int) -> Int {
        count += x
        return count
    }
    
}

This could be extended to classes and structs. Adding “pure” to a class or structs makes all of its methods pure.

The benefit of such feature is that you could gradually add more functional code that is checked by the compiler and it is optional. I am most probably missing a lot issues here. This is just a rough idea but I wanted to share it with the community to better understand the tradeoffs.

If this has already been discussed can anyone point me to that discussion?

Thanks!


(Robert Widmann) #2

It seems you've been beaten to the punch! https://github.com/apple/swift/blob/master/docs/HighLevelSILOptimizations.rst#id12

Please don't use these attributes in production code. They make things go fast, but they also destroy any hope of good debug info and can cause UB if used incorrectly.

~Robert Widmann

2016/09/08 14:19、Guido Marucci Blas via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

Hi, I wanted to discussed an idea that I’ve been thinking for the last couple of weeks. First I want to give you a little bit of context. I am the technical leader in the iOS project I am working on. We have been using Swift for almost one year and half. Most of the first part of the project was desinged by me. I am being trying to apply concepts from the functional paradigm and in particular we heavily depend on ReactiveCocoa. For the last year two less experienced developers joined the team. I was in charge of teaching them about iOS programming (this was the first time doing an iOS app for them), functional programming (they are still college student with a background in OOP) and the details of our code base.

Coming from a background in OOP and imperative programming makes the change to a more functional approach quite “interesting”. I really like Swift and its flexibility to be able to tackle both low level and high level problems. But some times this flexibility makes it more error prone when you try to apply functional programming concepts. For example I found my self reviewing and correcting pull request with things like “When you use functions like map or filter on a collection or a SignalProducer don’t inject side effects”.

This led me to the idea of introducing a @pure annotation for functions. Full disclosure: I have almost no background in compilers and the internals of the Swift language so I have no idea if this makes sense at all. Such a annotation (it could also be a keyword like override o mutating) will make the compiler check if the function is actually a pure function, where by Pure I mean

  - It doesn’t mutate any internal or global state
  - It doesn’t call any non-pure function
  - It must have a return type != from Void
  - It can access non mutable state

In this context a pure function is “stronger” than the mutating keyword in the sense that the pure function won’t allow any type of mutation even if you are calling a non-pure method (which performs a mutation or a side effect) on an object (reference type) instead of a struct. For example

final class Foo {
    
    private var count = 0
    
    func foo(x: Int) -> Int {
        count += x
        return count
    }
    
}

struct Bar {
    
    var count: Int
    var foo: Foo
    
    // This method performs a mutation
    // even though is not marked as mutating
    // You could call it a "transitive mutation".
    func bar(x: Int) -> Int {
        return foo.foo(x)
    }
    
    mutating func baz(x: Int) -> Int {
        count += x
        return count
    }
    
}

This could be extended to classes and structs. Adding “pure” to a class or structs makes all of its methods pure.

The benefit of such feature is that you could gradually add more functional code that is checked by the compiler and it is optional. I am most probably missing a lot issues here. This is just a rough idea but I wanted to share it with the community to better understand the tradeoffs.

If this has already been discussed can anyone point me to that discussion?

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


(David Rönnqvist) #3

It could be nice to have something similar to the @effects attribute, _but_ where the compiler could tell if the annotation was valid or not. Meaning that if I believe a function to be pure, I could annotate it as such. If the compiler notices that I'm reading from some global state, that annotation would be an error (possibly even with fix-its to either change the annotation (to read only) or to remove the line that is reading the global state).

One could make the argument that such an annotation should never be needed because if the compiler can tell that a function is pure, shouldn't it perform these optimizations anyway? But if a potential @pure annotation is used as a developer's way of verifying assumptions about their code ("my intention here is for this function to be pure") rather than being used to enable optimizations, then I do feel that it could be useful.

I have on a few occasions wanted an attribute like that (both "pure" and "read only").

- David

···

On 9 Sep 2016, at 18:22, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

It seems you've been beaten to the punch! https://github.com/apple/swift/blob/master/docs/HighLevelSILOptimizations.rst#id12

Please don't use these attributes in production code. They make things go fast, but they also destroy any hope of good debug info and can cause UB if used incorrectly.

~Robert Widmann

2016/09/08 14:19、Guido Marucci Blas via swift-evolution <swift-evolution@swift.org> のメッセージ:

Hi, I wanted to discussed an idea that I’ve been thinking for the last couple of weeks. First I want to give you a little bit of context. I am the technical leader in the iOS project I am working on. We have been using Swift for almost one year and half. Most of the first part of the project was desinged by me. I am being trying to apply concepts from the functional paradigm and in particular we heavily depend on ReactiveCocoa. For the last year two less experienced developers joined the team. I was in charge of teaching them about iOS programming (this was the first time doing an iOS app for them), functional programming (they are still college student with a background in OOP) and the details of our code base.

Coming from a background in OOP and imperative programming makes the change to a more functional approach quite “interesting”. I really like Swift and its flexibility to be able to tackle both low level and high level problems. But some times this flexibility makes it more error prone when you try to apply functional programming concepts. For example I found my self reviewing and correcting pull request with things like “When you use functions like map or filter on a collection or a SignalProducer don’t inject side effects”.

This led me to the idea of introducing a @pure annotation for functions. Full disclosure: I have almost no background in compilers and the internals of the Swift language so I have no idea if this makes sense at all. Such a annotation (it could also be a keyword like override o mutating) will make the compiler check if the function is actually a pure function, where by Pure I mean

  - It doesn’t mutate any internal or global state
  - It doesn’t call any non-pure function
  - It must have a return type != from Void
  - It can access non mutable state

In this context a pure function is “stronger” than the mutating keyword in the sense that the pure function won’t allow any type of mutation even if you are calling a non-pure method (which performs a mutation or a side effect) on an object (reference type) instead of a struct. For example

final class Foo {
    
    private var count = 0
    
    func foo(x: Int) -> Int {
        count += x
        return count
    }
    
}

struct Bar {
    
    var count: Int
    var foo: Foo
    
    // This method performs a mutation
    // even though is not marked as mutating
    // You could call it a "transitive mutation".
    func bar(x: Int) -> Int {
        return foo.foo(x)
    }
    
    mutating func baz(x: Int) -> Int {
        count += x
        return count
    }
    
}

This could be extended to classes and structs. Adding “pure” to a class or structs makes all of its methods pure.

The benefit of such feature is that you could gradually add more functional code that is checked by the compiler and it is optional. I am most probably missing a lot issues here. This is just a rough idea but I wanted to share it with the community to better understand the tradeoffs.

If this has already been discussed can anyone point me to that discussion?

Thanks!
_______________________________________________
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


(Xiaodi Wu) #4

This has been proposed several times before on this list. For example:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/003684.html

As I recall, in one of these previous threads, there was a very educational
discussion on highly technical details. It would be good to familiarize
oneself with that discussion. Unfortunately, I'm not able to retrieve the
link to that particular thread at the moment.

···

On Fri, Sep 9, 2016 at 3:08 PM, Guido Marucci Blas via swift-evolution < swift-evolution@swift.org> wrote:

Robert that is what I was thinking off. Scala does something similar to
what you say with the tail recursive annotation.

On Sep 9, 2016, at 4:28 PM, David Rönnqvist <david.ronnqvist@gmail.com> > wrote:

It could be nice to have something similar to the @effects attribute,
_but_ where the compiler could tell if the annotation was valid or not.
Meaning that if I believe a function to be pure, I could annotate it as
such. If the compiler notices that I'm reading from some global state, that
annotation would be an error (possibly even with fix-its to either change
the annotation (to read only) or to remove the line that is reading the
global state).

One could make the argument that such an annotation should never be needed
because if the compiler can tell that a function is pure, shouldn't it
perform these optimizations anyway? But if a potential @pure annotation is
used as a developer's way of verifying assumptions about their code ("my
intention here is for this function to be pure") rather than being used to
enable optimizations, then I do feel that it could be useful.

I have on a few occasions wanted an attribute like that (both "pure" and
"read only").

- David

On 9 Sep 2016, at 18:22, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:

It seems you've been beaten to the punch! https://github.com/
apple/swift/blob/master/docs/HighLevelSILOptimizations.rst#id12

Please don't use these attributes in production code. They make things go
fast, but they also destroy any hope of good debug info and can cause UB if
used incorrectly.

~Robert Widmann

2016/09/08 14:19、Guido Marucci Blas via swift-evolution <
swift-evolution@swift.org> のメッセージ:

Hi, I wanted to discussed an idea that I’ve been thinking for the last
couple of weeks. First I want to give you a little bit of context. I am the
technical leader in the iOS project I am working on. We have been using
Swift for almost one year and half. Most of the first part of the project
was desinged by me. I am being trying to apply concepts from the functional
paradigm and in particular we heavily depend on ReactiveCocoa. For the last
year two less experienced developers joined the team. I was in charge of
teaching them about iOS programming (this was the first time doing an iOS
app for them), functional programming (they are still college student with
a background in OOP) and the details of our code base.

Coming from a background in OOP and imperative programming makes the
change to a more functional approach quite “interesting”. I really like
Swift and its flexibility to be able to tackle both low level and high
level problems. But some times this flexibility makes it more error prone
when you try to apply functional programming concepts. For example I found
my self reviewing and correcting pull request with things like “When you
use functions like map or filter on a collection or a SignalProducer don’t
inject side effects”.

This led me to the idea of introducing a @pure annotation for functions.
Full disclosure: I have almost no background in compilers and the internals
of the Swift language so I have no idea if this makes sense at all. Such a
annotation (it could also be a keyword like override o mutating) will make
the compiler check if the function is actually a pure function, where by
Pure I mean

- It doesn’t mutate any internal or global state
- It doesn’t call any non-pure function
- It must have a return type != from Void
- It can access non mutable state

In this context a pure function is “stronger” than the mutating keyword in
the sense that the pure function won’t allow any type of mutation even if
you are calling a non-pure method (which performs a mutation or a side
effect) on an object (reference type) instead of a struct. For example

final class Foo {

    private var count = 0

    func foo(x: Int) -> Int {
        count += x
        return count
    }

}

struct Bar {

    var count: Int
    var foo: Foo

    // This method performs a mutation
    // even though is not marked as mutating
    // You could call it a "transitive mutation".
    func bar(x: Int) -> Int {
        return foo.foo(x)
    }

    mutating func baz(x: Int) -> Int {
        count += x
        return count
    }

}

This could be extended to classes and structs. Adding “pure” to a class or
structs makes all of its methods pure.

The benefit of such feature is that you could gradually add more
functional code that is checked by the compiler and it is optional. I am
most probably missing a lot issues here. This is just a rough idea but I
wanted to share it with the community to better understand the tradeoffs.

If this has already been discussed can anyone point me to that discussion?

Thanks!

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

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

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


(Guido Marucci Blas) #5

Robert that is what I was thinking off. Scala does something similar to what you say with the tail recursive annotation.

···

On Sep 9, 2016, at 4:28 PM, David Rönnqvist <david.ronnqvist@gmail.com> wrote:

It could be nice to have something similar to the @effects attribute, _but_ where the compiler could tell if the annotation was valid or not. Meaning that if I believe a function to be pure, I could annotate it as such. If the compiler notices that I'm reading from some global state, that annotation would be an error (possibly even with fix-its to either change the annotation (to read only) or to remove the line that is reading the global state).

One could make the argument that such an annotation should never be needed because if the compiler can tell that a function is pure, shouldn't it perform these optimizations anyway? But if a potential @pure annotation is used as a developer's way of verifying assumptions about their code ("my intention here is for this function to be pure") rather than being used to enable optimizations, then I do feel that it could be useful.

I have on a few occasions wanted an attribute like that (both "pure" and "read only").

- David

On 9 Sep 2016, at 18:22, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It seems you've been beaten to the punch! https://github.com/apple/swift/blob/master/docs/HighLevelSILOptimizations.rst#id12

Please don't use these attributes in production code. They make things go fast, but they also destroy any hope of good debug info and can cause UB if used incorrectly.

~Robert Widmann

2016/09/08 14:19、Guido Marucci Blas via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> のメッセージ:

Hi, I wanted to discussed an idea that I’ve been thinking for the last couple of weeks. First I want to give you a little bit of context. I am the technical leader in the iOS project I am working on. We have been using Swift for almost one year and half. Most of the first part of the project was desinged by me. I am being trying to apply concepts from the functional paradigm and in particular we heavily depend on ReactiveCocoa. For the last year two less experienced developers joined the team. I was in charge of teaching them about iOS programming (this was the first time doing an iOS app for them), functional programming (they are still college student with a background in OOP) and the details of our code base.

Coming from a background in OOP and imperative programming makes the change to a more functional approach quite “interesting”. I really like Swift and its flexibility to be able to tackle both low level and high level problems. But some times this flexibility makes it more error prone when you try to apply functional programming concepts. For example I found my self reviewing and correcting pull request with things like “When you use functions like map or filter on a collection or a SignalProducer don’t inject side effects”.

This led me to the idea of introducing a @pure annotation for functions. Full disclosure: I have almost no background in compilers and the internals of the Swift language so I have no idea if this makes sense at all. Such a annotation (it could also be a keyword like override o mutating) will make the compiler check if the function is actually a pure function, where by Pure I mean

  - It doesn’t mutate any internal or global state
  - It doesn’t call any non-pure function
  - It must have a return type != from Void
  - It can access non mutable state

In this context a pure function is “stronger” than the mutating keyword in the sense that the pure function won’t allow any type of mutation even if you are calling a non-pure method (which performs a mutation or a side effect) on an object (reference type) instead of a struct. For example

final class Foo {
    
    private var count = 0
    
    func foo(x: Int) -> Int {
        count += x
        return count
    }
    
}

struct Bar {
    
    var count: Int
    var foo: Foo
    
    // This method performs a mutation
    // even though is not marked as mutating
    // You could call it a "transitive mutation".
    func bar(x: Int) -> Int {
        return foo.foo(x)
    }
    
    mutating func baz(x: Int) -> Int {
        count += x
        return count
    }
    
}

This could be extended to classes and structs. Adding “pure” to a class or structs makes all of its methods pure.

The benefit of such feature is that you could gradually add more functional code that is checked by the compiler and it is optional. I am most probably missing a lot issues here. This is just a rough idea but I wanted to share it with the community to better understand the tradeoffs.

If this has already been discussed can anyone point me to that discussion?

Thanks!
_______________________________________________
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