[swift-evolution] [Pitch] Flattening the function type of unapplied instance methods

Kevin Lundberg kevin at klundberg.com
Mon Feb 22 17:11:33 CST 2016


I personally use the existing behavior in some of my code, by providing the reference to the method directly to a method/function that takes a closure as a parameter. A modified excerpt from my code:

    let observer = NSNotificationCenter.defaultCenter().addObserverForName(LoginSuccess, object: nil, queue: nil, usingBlock: weakify(self, self.dynamicType.userDidLogin))

(Note: This usage normally would lead to a retain cycle, but I wrap the method reference in this weakify() function which handles weakly applying self to the returned method reference.)

Un-currying method references would make this pattern harder to apply, so if there was a way to resolve this behavior without removing the current flexibility that the curried behavior offers, I would prefer that. Un-currying mutating methods only would be one option, but that seems a bit ugly. 

However, if there isn't a way to fix it without changing existing behavior, I'd prefer the un-curried form everywhere to removing static method access outright.

Kevin Lundberg
kevin at klundberg.com



> On Feb 22, 2016, at 5:52 PM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Today, you can reference an instance property as a member of its type to access it as a fully unbound function, which is currently curried:
> 
> struct Foo {
>   var x: Int
>   func foo(y y: Int) { return x + y }
> }
> 
> let foo = Foo.foo
> foo(Foo(x: 1))(y: 2) // returns 3
> 
> However, this is problematic for `mutating` methods. Since the first argument is `inout`, the mutation window for the parameter formally ends before the second argument can be applied to complete the call. Currently we miscompile this, and form a closure over a dangling pointer, leading to undefined behavior:
> 
> struct Bar {
>   var x = 0
>   mutating func bar() { x += 1 }
> }
> 
> let bar = Bar.bar
> var a = Bar()
> bar(&a)() // This might appear to work, if we don't optimize too hard
> 
> let closure: () -> ()
> do {
>   var b = Bar()
>   closure = bar(&b)
> }
> closure() // This scribbles dead stack space
> 
> var c = Bar() {
>   didSet { print("c was set") }
> }
> 
> bar(&c)() // This will scribble over c after didSet is called, if not worse
> 
> We can close this hole by disallowing a reference to Bar.bar, like we already disallow partial applications. However, I think it would be in line with our other simplifications of the function type system to change the type of `Bar.bar` and other unapplied instance method references to no longer be curried. In addition to providing a model for unapplied instance methods that works with mutating methods, this would also eliminate a type difference between free functions and methods of the same arity, allowing for easier code reuse. For instance, `reduce` takes a closure of type (State, Element) -> State. Flattening the formal type of instance methods would allow binary methods to be used as-is with `reduce`, like binary free functions can:
> 
> func sumOfInts(ints: [Int]) -> Int { 
>   return ints.reduce(0, combine: +)
> }
> func unionOfSets<T>(sets: [Set<T>]) -> Set<T> { 
>   return ints.reduce([], combine: Set.union)
> }
> 
> What do you all think?
> 
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160222/151682ba/attachment.html>


More information about the swift-evolution mailing list