3

In Swift 5, what is a way to compare pointers to two closures?

I was told here In Swift 3, what is a way to compare two closures?

that this is somehow a different question. So now you have it.

public typealias Action = () -> Void

let action1: Action = {}
let action2: Action = {}

assert(action1 == action1)
assert(action1 != action2)
Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
  • 1
    What do you mean with _pointers_ to closures? Could you share a code example as well? – Joakim Danielson Oct 23 '20 at 08:38
  • this: void (foo*)(); is what I mean by pointers. Just a plain old C pointer to function. – Anton Tropashko Oct 29 '20 at 11:23
  • That's why I asked since we don't use pointers in swift (at least not in this way) – Joakim Danielson Oct 29 '20 at 12:38
  • That's why we have trivial problems that can't be resolved in a straightforward manner. The outcome of "design by comittee" where anyone hardly cares about the whole picture. – Anton Tropashko Oct 29 '20 at 14:05
  • Well you can always program in C then. And I honestly don't understand why you need to verify that two closures are equal, isn't the whole point of closures that they can be anything as long as they match the given signature. – Joakim Danielson Oct 29 '20 at 14:17
  • You got it exactly right, the whole point that a significant chunk of people prefer C and Obj-C is cause you (as in: the blob of people who have designed swift) fail to understand why this could be useful to someone. This goes for a wide plethora of language features. – Anton Tropashko Oct 29 '20 at 14:40
  • And this is one of the subtleties why ObjC is elegant and Swift is anything but. Despite being full of modern language bells and whistles. – Anton Tropashko Oct 29 '20 at 14:42

1 Answers1

4

About pointers: notably, objects (classes) are reference types, so the equality of two objects is not the same as the identity of two objects. For equality (if defined within the class), you have the == operator, and for identity, the === operator:

class Foo:Equatable {
    var i:Int = 0
    static func == (lhs: Foo, rhs: Foo) -> Bool {
        return lhs.i == rhs.i
    }
}

let foo1 = Foo()
let foo2 = Foo()
let referenceToFoo1 = foo1

foo1 == foo2 // equality: true, since foo1.i == foo2.i, which is the criteria for equality
foo1 === foo2 // identity: false, since they are two different instances
foo1 === referenceToFoo1 // identity: true, both variables point to the same instance
foo1.i = 2
print(referenceToFoo1.i) // 2, since it refers to the same object

"Reference" can also be called a "pointer," although in Swift, unlike in C, we don't have to delve into pointer arithmetic, which is a pretty low-level way of dealing with memory addresses of pointers to objects and other data.


Just like classes, closures are also reference types in Swift, so in addition to their "equality" (do they contain the same code and captured information etc.) we can also look into their identity (i.e. do these two variables refer to the same closure or two different ones, even if they look the same etc.).

Problem is, Swift doesn't really seem to want to help us there.

Just like == doesn't work for closures, neither does ===. Neither do closure-types seem to conform to any protocol (and note that there's no single "closure type" but rather an infinite multitude of them depending on parameters and return types).

Furthermore, even casting a closure to AnyObject doesn't work:

foo1 as AnyObject === referenceToFoo1 as AnyObject // true, as expected

// with the `action1` closure from the question:
action1 as AnyObject === action1 as AnyObject // false; a bit of a surprise

Looks like every time Swift casts a closure to AnyObject, it creates a new AnyObject instance for some reason… So comparing these also reveals nothing.

So… I don't think we can reason about the identity or equality of closures in Swift. Maybe there is some other way, possibly via unsafe pointers… well, let me know if anyone had any luck going down that rabbit hole!

pua666
  • 326
  • 1
  • 7
  • "Just like classes, closures are also reference types in Swift" How would you ever tell whether closures were value types or reference types? – newacct May 02 '21 at 22:24
  • @newacct https://docs.swift.org/swift-book/LanguageGuide/Closures.html See the chapter whose title is literally “Closures Are Reference Types” – pua666 May 03 '21 at 08:28
  • Nothing in that section supports the assertion that closures are reference types. The fact that one closure variable assigned to another variable, both share the same captured variable, just shows that the closures capture variables by reference. A C++ lambda, which is a value type, which captures by reference, exhibits the same behavior (one lambda variable, assigned to another, will share the same captured variable). – newacct Jun 01 '21 at 03:52
  • @newacct Once again: the official documentation of the Swift language, written by its creators, states, in a section's title, that "Closures Are Reference Types." I'd think that it's proof enough that closures in Swift are, um, reference types, but then what do I know? – pua666 Sep 23 '21 at 02:36
  • Language semantics are not taken on faith just because someone says so. It must be possible to verify them based on the actual behavior of the language. The behavior described in the documentation (when one closure variable assigned to another variable, both share the same captured variable) is demonstrably compatible with closures being value types, and does not verify that closures are reference types, and Swift devlopers have [agreed](https://bugs.swift.org/browse/SR-8733?focusedCommentId=39644&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-39644). – newacct Sep 23 '21 at 06:53
  • If anyone thinks they have a way to verify that closures are reference types, I would love to hear it. If nobody can come up with a way to distinguish closures being reference types or value types, then perhaps it is meaningless to claim one way or the other. – newacct Sep 23 '21 at 06:57