zoul
zoul

Reputation: 104065

Why does an instance method have this type in Swift?

Given this code:

struct Foo {
    func f() {}
}

let f = Foo.f // (Foo) -> () -> ()

Why does f have the type (Foo) -> () -> () and not (Foo) -> ()? Wouldn’t it make sense for instance methods like Foo.f to be directly interchangeable with free functions of type (Foo) -> …?

Upvotes: 1

Views: 104

Answers (2)

Hamish
Hamish

Reputation: 80801

Why does f have the type (Foo) -> () -> () and not (Foo) -> ()?

That's just currently how unapplied instance method references are implemented; they're curried functions that follow the model of "give me an instance, and I'll give you back a partially-applied instance method" (partially applied with that instance).

However this is problematic in some areas, firstly because it's generally more useful for them to be of the form (Self, Args...) -> Ret, but also more importantly because it because it leads to issues around mutating methods. These end up looking like (inout Self) -> (Args...) -> Ret with the current system, which is problematic because the window of mutation for inout only lasts for the duration of the call.

This means the following currently compiles, but is actually undefined behaviour:

struct S {
  var i: Int

  mutating func increment() {
    i += 1
  }
}

var s = S(i: 0)
let unappliedIncrement = S.increment
let increment = unappliedIncrement(&s)
increment() // undefined behaviour

These are the main motivations behind SE-0042, which will change unapplied instance method references from being of the form (Self) -> (Args...) -> Ret to being of the form
(Self, Args...) -> Ret (and with inout, Self will be inout Self – allowing the mutation of the instance without UB).

This proposal is yet to be implemented, but once it is, assuming empty parameter lists get flattened out (so you don't end up with a trailing Void parameter), Foo.f will indeed be of type
(Foo) -> Void.

Upvotes: 2

matt
matt

Reputation: 535178

It's because, by saying this:

let f = Foo.f

you've described f as a class method, Foo.f. But in your declaration of Foo, f is an instance method. You have thus accidentally discovered the deep dark secret that instance methods are class methods in disguise; you are seeing the signature of the secret class method.

If you had said

let f = Foo().f

you would have gotten the expected result.

Upvotes: 0

Related Questions