[Pitch] Flattening the function type of unapplied instance methods

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

1 Like

+1; it's about time.

···

on Mon Feb 22 2016, Joe Groff <swift-evolution@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?

--
-Dave

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@klundberg.com

···

On Feb 22, 2016, at 5:52 PM, Joe Groff via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

At a glance it sounds great!

The current behaviour should clearly be changed and I'm very much in favor keeping the ability to get unapplied instance methods for mutating methods.

I particularly liked the last example. I've tried similar things before only to be disappointed when it didn't work.

- Janosch

···

On 22 Feb 2016, at 23:52, Joe Groff via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1

-Thorsten

···

Am 22.02.2016 um 23:52 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org>:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1. It is very obscure that you can do this!

···

On Tuesday, 23 February 2016, Joe Groff via swift-evolution < swift-evolution@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

--
-- Howard.

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

Wow, I thought we were banning this, along with the other unsupported partial application cases. I’d strongly suggest doing that *now* (independently of how the design topic is resolved) since it is such an open tar pit.

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.

I see a few possible design points:

1. Disallow partial application of mutating methods.
2. Change all partially applied methods to be an uncurried form (as you propose).
3. Change mutating methods to be uncurried, but leave everything else curried.
4. Change methods on structs/enums(protocols?) to be uncurried, but leave classes curried.

#1 is the safe path.
#2 is the nuclear path.
#3/#4 are the weirdly inconsistent paths.

None of these are really great. Since you’re advocating for it, I’m very concerned about making partially applied methods be uncurried always (#2). I have seen a *lot* of people use curried methods on classes (in particular) and this leads to some pretty elegant patterns. I’d expect any serious discussion of changing this behavior to include a survey of what people are actually using this for, and whether the end result we’d get from this change would be better or worse overall (not just in a single case).

-Chris

···

On Feb 22, 2016, at 2:52 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think this would nicely combine with what is being said in this other thread:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/011132.html

I.e. it would allow to define an asymmetric method like

public protocol IntegerArithmeticType {
    public func adding(to: Self) -> Self
}

and then access it as a symmetric method to implement an operator:

public func +<T: IntegerArithmeticType>(lhs: T, rhs: T) -> T {
    return T.adding(lhs, rhs)
}

it would make everything more consistent, I think.

···


Nicola

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

If I understand the intent of `weakify` correctly, I think this kind of use case is already pretty severely curtailed by our removing the tuple splat feature, since you can no longer make `weakify` generic across multiple method signatures. Like the tuple splat feature, an explicit variadic splatting feature ought to make this possible for flattened method signatures:

func weakify<Class: class, Args, Result>(instance: Class, method: (Class, Args...) -> Result) -> (Args...) -> Result {
  return {[weak instance] args... in method(instance, args...) }
}

-Joe

···

On Feb 22, 2016, at 3:11 PM, Kevin Lundberg <kevin@klundberg.com> wrote:

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.

Agreed on the tuple splat removal point, but the common use cases I use
this for today don't apply weakify to methods with more than one
argument in practice. Even so, your example here is admittedly a pretty
simple change to make to keep existing functionality if the curried form
of this goes away, and this language change would make it haeder for
people who want to do something similar to create a retain cycle
scenario if they don't use something like weakify to safely pass method
references around.

I do like the current behavior, but given this you've convinced me that
it may be best for it to change :slight_smile:

- Kevin

···

On 2/22/2016 6:18 PM, Joe Groff wrote:

If I understand the intent of `weakify` correctly, I think this kind
of use case is already pretty severely curtailed by our removing the
tuple splat feature, since you can no longer make `weakify` generic
across multiple method signatures. Like the tuple splat feature, an
explicit variadic splatting feature ought to make this possible for
flattened method signatures:

    func weakify<Class: class, Args, Result>(instance: Class, method:
    (Class, Args...) -> Result) -> (Args...) -> Result {
      return {[weak instance] args... in method(instance, args...) }
    }

-Joe

I'm concerned about that too, but I think most of these use cases are already impeded by removing tuple argument splatting, since that makes it impossible to make a generic method transformation like `alterMethod<Receiver, Args, Ret>(method: Receiver -> Args -> Ret)`. I think these use cases can be recovered on flattened methods by explicit variadic splatting, since you could then write `alterMethod<Receiver, Args, Ret>(method: (Receiver, Args...) -> Ret)` instead.

-Joe

···

On Feb 24, 2016, at 8:50 PM, Chris Lattner <clattner@apple.com> wrote:

None of these are really great. Since you’re advocating for it, I’m very concerned about making partially applied methods be uncurried always (#2). I have seen a *lot* of people use curried methods on classes (in particular) and this leads to some pretty elegant patterns. I’d expect any serious discussion of changing this behavior to include a survey of what people are actually using this for, and whether the end result we’d get from this change would be better or worse overall (not just in a single case).

+1 for this. A clear win with regard to simplification.

···

On Mon, Feb 22, 2016 at 8:41 PM, Kevin Lundberg via swift-evolution < swift-evolution@swift.org> wrote:

On 2/22/2016 6:18 PM, Joe Groff wrote:

If I understand the intent of `weakify` correctly, I think this kind of
use case is already pretty severely curtailed by our removing the tuple
splat feature, since you can no longer make `weakify` generic across
multiple method signatures. Like the tuple splat feature, an explicit
variadic splatting feature ought to make this possible for flattened method
signatures:

func weakify<Class: class, Args, Result>(instance: Class, method: (Class,
Args...) -> Result) -> (Args...) -> Result {
  return {[weak instance] args... in method(instance, args...) }
}

-Joe

Agreed on the tuple splat removal point, but the common use cases I use
this for today don't apply weakify to methods with more than one argument
in practice. Even so, your example here is admittedly a pretty simple
change to make to keep existing functionality if the curried form of this
goes away, and this language change would make it haeder for people who
want to do something similar to create a retain cycle scenario if they
don't use something like weakify to safely pass method references around.

I do like the current behavior, but given this you've convinced me that it
may be best for it to change :slight_smile:

- Kevin

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

None of these are really great. Since you’re advocating for it, I’m very concerned about making partially applied methods be uncurried always (#2). I have seen a *lot* of people use curried methods on classes (in particular) and this leads to some pretty elegant patterns. I’d expect any serious discussion of changing this behavior to include a survey of what people are actually using this for, and whether the end result we’d get from this change would be better or worse overall (not just in a single case).

I'm concerned about that too, but I think most of these use cases are already impeded by removing tuple argument splatting, since that makes it impossible to make a generic method transformation like `alterMethod<Receiver, Args, Ret>(method: Receiver -> Args -> Ret)`. I think these use cases can be recovered on flattened methods by explicit variadic splatting, since you could then write `alterMethod<Receiver, Args, Ret>(method: (Receiver, Args...) -> Ret)` instead.

I don’t really understand that, because the use-cases I’ve seen aren’t in generic context’s, but let me step back and see if I understand your proposal. Here’s your example again:

struct Bar {
  var x = 0
  mutating func bar() { x += 1 }
}

let a = Bar.bar

I believe that you’re proposing that “a” have type "(inout Bar) -> ()” since the empty argument list got flattened into the self argument.

However, what is the type of the partial application case, i.e. “someBarInstance.bar”?

It seems that this *has* to be invalid for mutating methods, and it is the most commonly curried forms that I’ve seen for classes.

I would be extremely nervous about saying that partial application and the raw name disagree, and I really wouldn’t want to kill partial application for classes and non-mutating methods.

I think that the least bad answer here is to just have sema reject attempts to get the base name of the mutating methods. In fact, my memory was partially right. We *are* already rejecting the partial application case:

  5> var b = Bar()
b: Bar = {
  x = 0
}
  6> b.bar
repl.swift:6:3: error: partial application of 'mutating' method is not allowed

so I think that we are just missing a simple case that we should be rejecting here.

-Chris

···

On Feb 25, 2016, at 9:13 AM, Joe Groff <jgroff@apple.com> wrote:

On Feb 24, 2016, at 8:50 PM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

None of these are really great. Since you’re advocating for it, I’m very concerned about making partially applied methods be uncurried always (#2). I have seen a *lot* of people use curried methods on classes (in particular) and this leads to some pretty elegant patterns. I’d expect any serious discussion of changing this behavior to include a survey of what people are actually using this for, and whether the end result we’d get from this change would be better or worse overall (not just in a single case).

I'm concerned about that too, but I think most of these use cases are already impeded by removing tuple argument splatting, since that makes it impossible to make a generic method transformation like `alterMethod<Receiver, Args, Ret>(method: Receiver -> Args -> Ret)`. I think these use cases can be recovered on flattened methods by explicit variadic splatting, since you could then write `alterMethod<Receiver, Args, Ret>(method: (Receiver, Args...) -> Ret)` instead.

I don’t really understand that, because the use-cases I’ve seen aren’t in generic context’s

Do you have an example in mind of a nongeneric use of curried methods that can't be easily rewritten in terms of a flat method type?

, but let me step back and see if I understand your proposal. Here’s your example again:

struct Bar {
  var x = 0
  mutating func bar() { x += 1 }
}

let a = Bar.bar

I believe that you’re proposing that “a” have type "(inout Bar) -> ()” since the empty argument list got flattened into the self argument.

However, what is the type of the partial application case, i.e. “someBarInstance.bar”?

It seems that this *has* to be invalid for mutating methods, and it is the most commonly curried forms that I’ve seen for classes.

Yes, the partial application is not allowed. We currently incorrectly accept the fully applied reference `Bar.bar`. I agree that if we change nothing else, that's a hole we should plug.

I would be extremely nervous about saying that partial application and the raw name disagree, and I really wouldn’t want to kill partial application for classes and non-mutating methods.

We can decouple the meaning of `Type.instanceMethod` from the partial application `instance.instanceMethod`. Even if the type of `Type.instanceMethod` is flattened, we can still accept `instance.instanceMethod` with the current behavior. It's maybe a bit less elegant that `instance.instanceMethod` isn't just "sugar" for `Type.instanceMethod(instance)`, but that already isn't really the case for `mutating` methods—we treat a full application `instance.instanceMethod(foo)` as a single call for the purposes of the mutation model.

-Joe

···

On Feb 25, 2016, at 4:22 PM, Chris Lattner <clattner@apple.com> wrote:

On Feb 25, 2016, at 9:13 AM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Feb 24, 2016, at 8:50 PM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

I think that the least bad answer here is to just have sema reject attempts to get the base name of the mutating methods. In fact, my memory was partially right. We *are* already rejecting the partial application case:

  5> var b = Bar()
b: Bar = {
  x = 0
}
  6> b.bar
repl.swift:6:3: error: partial application of 'mutating' method is not allowed

so I think that we are just missing a simple case that we should be rejecting here.

-Chris

I may be off-track, but you mentioned the binary tree example of passing methods as binary closures. Wouldn't the converse be true for wedging state into unary func using existing class methods?

func visit(numbers: [Int], predicate: (Int) -> Bool) {
  numbers.lazy.filter(predicate).forEach({ print($0) })
}

// For some reason not a closure...
class FilterNumbersWithRejects {
  var rejects = [Int]()

  func check(val: Int) -> Bool {
    if (val < 100) { return true }
    rejects.append(val)
    return false
  }
}

let filterNumbers = FilterNumbersWithRejects()
visit([3, 33, 333], predicate: filterNumbers.check)
print(filterNumbers.rejects)

-Andy

···

On Feb 25, 2016, at 4:57 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I'm concerned about that too, but I think most of these use cases are already impeded by removing tuple argument splatting, since that makes it impossible to make a generic method transformation like `alterMethod<Receiver, Args, Ret>(method: Receiver -> Args -> Ret)`. I think these use cases can be recovered on flattened methods by explicit variadic splatting, since you could then write `alterMethod<Receiver, Args, Ret>(method: (Receiver, Args...) -> Ret)` instead.

I don’t really understand that, because the use-cases I’ve seen aren’t in generic context’s

Do you have an example in mind of a nongeneric use of curried methods that can't be easily rewritten in terms of a flat method type?

I'm not proposing that we take the ability to use `filterNumbers.check` away. I'm proposing changing the type of the fully unapplied reference FilterNumbersWithRejects.check.

-Joe

···

On Feb 25, 2016, at 10:16 PM, Andrew Trick <atrick@apple.com> wrote:

On Feb 25, 2016, at 4:57 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'm concerned about that too, but I think most of these use cases are already impeded by removing tuple argument splatting, since that makes it impossible to make a generic method transformation like `alterMethod<Receiver, Args, Ret>(method: Receiver -> Args -> Ret)`. I think these use cases can be recovered on flattened methods by explicit variadic splatting, since you could then write `alterMethod<Receiver, Args, Ret>(method: (Receiver, Args...) -> Ret)` instead.

I don’t really understand that, because the use-cases I’ve seen aren’t in generic context’s

Do you have an example in mind of a nongeneric use of curried methods that can't be easily rewritten in terms of a flat method type?

I may be off-track, but you mentioned the binary tree example of passing methods as binary closures. Wouldn't the converse be true for wedging state into unary func using existing class methods?

func visit(numbers: [Int], predicate: (Int) -> Bool) {
  numbers.lazy.filter(predicate).forEach({ print($0) })
}

// For some reason not a closure...
class FilterNumbersWithRejects {
  var rejects = [Int]()

  func check(val: Int) -> Bool {
    if (val < 100) { return true }
    rejects.append(val)
    return false
  }
}

let filterNumbers = FilterNumbersWithRejects()
visit([3, 33, 333], predicate: filterNumbers.check)
print(filterNumbers.rejects)

This was the core of my misunderstanding. I didn’t realize that you were proposing to break the symmetry between Type.method and instance.method.

-Chris

···

On Feb 26, 2016, at 9:44 AM, Joe Groff <jgroff@apple.com> wrote:

let filterNumbers = FilterNumbersWithRejects()
visit([3, 33, 333], predicate: filterNumbers.check)
print(filterNumbers.rejects)

I'm not proposing that we take the ability to use `filterNumbers.check` away. I'm proposing changing the type of the fully unapplied reference FilterNumbersWithRejects.check.

Joe explained this to me in a calming tone of voice and used small words that I was able to understand, I’m onboard now :-)

-Chris

···

On Feb 26, 2016, at 10:20 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 26, 2016, at 9:44 AM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

let filterNumbers = FilterNumbersWithRejects()
visit([3, 33, 333], predicate: filterNumbers.check)
print(filterNumbers.rejects)

I'm not proposing that we take the ability to use `filterNumbers.check` away. I'm proposing changing the type of the fully unapplied reference FilterNumbersWithRejects.check.

This was the core of my misunderstanding. I didn’t realize that you were proposing to break the symmetry between Type.method and instance.method.

May I resurrect this thread? Is it even going to be possible if we do it after ABI stability?

I am currently being bitten by:

This proposal has been reviewed and accepted as SE-0042. It lacks implementation; this is tracked by SR-1051.

1 Like