How do you test functions and closures for equality?
![Multi tool use Multi tool use](http://sgv.ssvwv.com/sg/ssvwvcomimagb.png)
Multi tool use
The book says that "functions and closures are reference types". So, how do you find out if the references are equal? == and === don't work.
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Here is how the Catterwauls are dealing with this:
MultiClosures & Equatable Closures
tests
closures swift equality
|
show 3 more comments
The book says that "functions and closures are reference types". So, how do you find out if the references are equal? == and === don't work.
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Here is how the Catterwauls are dealing with this:
MultiClosures & Equatable Closures
tests
closures swift equality
5
As far as I can tell, you also can't check equality of metaclasses (e.g.,MyClass.self
)
– Jiaaro
Jun 9 '14 at 0:55
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
1
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
2
Great question, but totally separate thing: your use of a diacritic onå
to referencea
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)
– Rob Napier
Jun 11 '14 at 12:50
2
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50
|
show 3 more comments
The book says that "functions and closures are reference types". So, how do you find out if the references are equal? == and === don't work.
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Here is how the Catterwauls are dealing with this:
MultiClosures & Equatable Closures
tests
closures swift equality
The book says that "functions and closures are reference types". So, how do you find out if the references are equal? == and === don't work.
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Here is how the Catterwauls are dealing with this:
MultiClosures & Equatable Closures
tests
closures swift equality
closures swift equality
edited Jan 21 '16 at 16:19
Jessy
asked Jun 8 '14 at 23:38
JessyJessy
3,64612027
3,64612027
5
As far as I can tell, you also can't check equality of metaclasses (e.g.,MyClass.self
)
– Jiaaro
Jun 9 '14 at 0:55
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
1
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
2
Great question, but totally separate thing: your use of a diacritic onå
to referencea
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)
– Rob Napier
Jun 11 '14 at 12:50
2
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50
|
show 3 more comments
5
As far as I can tell, you also can't check equality of metaclasses (e.g.,MyClass.self
)
– Jiaaro
Jun 9 '14 at 0:55
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
1
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
2
Great question, but totally separate thing: your use of a diacritic onå
to referencea
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)
– Rob Napier
Jun 11 '14 at 12:50
2
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50
5
5
As far as I can tell, you also can't check equality of metaclasses (e.g.,
MyClass.self
)– Jiaaro
Jun 9 '14 at 0:55
As far as I can tell, you also can't check equality of metaclasses (e.g.,
MyClass.self
)– Jiaaro
Jun 9 '14 at 0:55
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
1
1
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
2
2
Great question, but totally separate thing: your use of a diacritic on
å
to reference a
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)– Rob Napier
Jun 11 '14 at 12:50
Great question, but totally separate thing: your use of a diacritic on
å
to reference a
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)– Rob Napier
Jun 11 '14 at 12:50
2
2
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50
|
show 3 more comments
8 Answers
8
active
oldest
votes
Chris Lattner wrote on the developer forums:
This is a feature we intentionally do not want to support. There are
a variety of things that will cause pointer equality of functions (in
the swift type system sense, which includes several kinds of closures)
to fail or change depending on optimization. If "===" were defined on
functions, the compiler would not be allowed to merge identical method
bodies, share thunks, and perform certain capture optimizations in
closures. Further, equality of this sort would be extremely
surprising in some generics contexts, where you can get reabstraction
thunks that adjust the actual signature of a function to the one the
function type expects.
https://devforums.apple.com/message/1035180#1035180
This means that you should not even try to compare closures for equality because optimizations may affect the outcome.
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
add a comment |
Simplest way is designate the block type as @objc_block
, and now you can cast it to an AnyObject which is comparable with ===
. Example:
typealias Ftype = @objc_block (s:String) -> ()
let f : Ftype = {
ss in
println(ss)
}
let ff : Ftype = {
sss in
println(sss)
}
let obj1 = unsafeBitCast(f, AnyObject.self)
let obj2 = unsafeBitCast(ff, AnyObject.self)
let obj3 = unsafeBitCast(f, AnyObject.self)
println(obj1 === obj2) // false
println(obj1 === obj3) // true
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
add a comment |
I've been looking for the answer, too. And I've found it at last.
- https://gist.github.com/dankogai/b03319ce427544beb5a4
What you need is the actual function pointer and its context hidden in the function object.
func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
typealias IntInt = (Int, Int)
let (hi, lo) = unsafeBitCast(f, IntInt.self)
let offset = sizeof(Int) == 8 ? 16 : 12
let ptr = UnsafePointer<Int>(lo+offset)
return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
return tl.0 == tr.0 && tl.1 == tr.1
}
And here is the demo:
// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f; println("(f === g) == (f === g)")
f = genericId; println("(f === g) == (f === g)")
f = g; println("(f === g) == (f === g)")
// closures
func mkcounter()->()->Int {
var count = 0;
return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == (peekFunc(c0))")
println("peekFunc(c1) == (peekFunc(c1))")
println("peekFunc(c2) == (peekFunc(c2))")
println("(c0() == c1()) == (c0() == c1())") // true : both are called once
println("(c0() == c2()) == (c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == (c0 === c1)")
println("(c0 === c2) == (c0 === c2)")
See the URLs below to find why and how it works:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
As you see it is capable of checking identity only (the 2nd test yields false
). But that should be good enough.
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
add a comment |
I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
add a comment |
This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast
examples, most of them don't show the full picture, here's a more detailed one:
typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()
func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
let objA = unsafeBitCast(a, AnyObject.self)
let objB = unsafeBitCast(b, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testAnyBlock(a: Any?, _ b: Any?) -> String {
if !(a is ObjBlock) || !(b is ObjBlock) {
return "a nor b are ObjBlock, they are not equal"
}
let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
class Foo
{
lazy var swfBlock: ObjBlock = self.swf
func swf() { print("swf") }
@objc func obj() { print("obj") }
}
let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()
print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.
I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block)
on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you haveStruct S { func f(_: Int) -> Bool }
, you actually have a function of typeS.f
which has type(S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding theself
parameter by calling the method on an object, e.g.S().f
, or by explicitly binding it, e.g.S.f(S())
), you create a new closure object. This object stores a pointer toS.f
(which can be shared), but also to your instance (
self, the
S()`).
– Alexander
Jan 3 at 4:41
This closure object must be unique per instance ofS
. If closure pointer equality were possible, then you would be surprised to discover thats1.f
is not the same pointer ass2.f
(because one is a closure object which referencess1
andf
, and the other is a closure object which referencess2
andf
).
– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
add a comment |
Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):
Swift:
typealias Callback = (Any...)->Void
class Command {
init(_ fn: @escaping Callback) {
self.fn_ = fn
}
var exec : (_ args: Any...)->Void {
get {
return fn_
}
}
var fn_ :Callback
}
let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
print(args.count)
}
cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")
cmd1 === cmd2 // true
cmd1 === cmd3 // false
Java:
interface Command {
void exec(Object... args);
}
Command cmd1 = new Command() {
public void exec(Object... args) [
// do something
}
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
public void exec(Object... args) {
// do something else
}
}
cmd1 == cmd2 // true
cmd1 == cmd3 // false
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
add a comment |
Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:
As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self
):
But – and this is just an idea – I can't help but notice that the where
clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?
add a comment |
Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).
So something like this:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
Now you just need to store the key
returned by the "register" function and pass it when unregistering.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f24111984%2fhow-do-you-test-functions-and-closures-for-equality%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
8 Answers
8
active
oldest
votes
8 Answers
8
active
oldest
votes
active
oldest
votes
active
oldest
votes
Chris Lattner wrote on the developer forums:
This is a feature we intentionally do not want to support. There are
a variety of things that will cause pointer equality of functions (in
the swift type system sense, which includes several kinds of closures)
to fail or change depending on optimization. If "===" were defined on
functions, the compiler would not be allowed to merge identical method
bodies, share thunks, and perform certain capture optimizations in
closures. Further, equality of this sort would be extremely
surprising in some generics contexts, where you can get reabstraction
thunks that adjust the actual signature of a function to the one the
function type expects.
https://devforums.apple.com/message/1035180#1035180
This means that you should not even try to compare closures for equality because optimizations may affect the outcome.
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
add a comment |
Chris Lattner wrote on the developer forums:
This is a feature we intentionally do not want to support. There are
a variety of things that will cause pointer equality of functions (in
the swift type system sense, which includes several kinds of closures)
to fail or change depending on optimization. If "===" were defined on
functions, the compiler would not be allowed to merge identical method
bodies, share thunks, and perform certain capture optimizations in
closures. Further, equality of this sort would be extremely
surprising in some generics contexts, where you can get reabstraction
thunks that adjust the actual signature of a function to the one the
function type expects.
https://devforums.apple.com/message/1035180#1035180
This means that you should not even try to compare closures for equality because optimizations may affect the outcome.
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
add a comment |
Chris Lattner wrote on the developer forums:
This is a feature we intentionally do not want to support. There are
a variety of things that will cause pointer equality of functions (in
the swift type system sense, which includes several kinds of closures)
to fail or change depending on optimization. If "===" were defined on
functions, the compiler would not be allowed to merge identical method
bodies, share thunks, and perform certain capture optimizations in
closures. Further, equality of this sort would be extremely
surprising in some generics contexts, where you can get reabstraction
thunks that adjust the actual signature of a function to the one the
function type expects.
https://devforums.apple.com/message/1035180#1035180
This means that you should not even try to compare closures for equality because optimizations may affect the outcome.
Chris Lattner wrote on the developer forums:
This is a feature we intentionally do not want to support. There are
a variety of things that will cause pointer equality of functions (in
the swift type system sense, which includes several kinds of closures)
to fail or change depending on optimization. If "===" were defined on
functions, the compiler would not be allowed to merge identical method
bodies, share thunks, and perform certain capture optimizations in
closures. Further, equality of this sort would be extremely
surprising in some generics contexts, where you can get reabstraction
thunks that adjust the actual signature of a function to the one the
function type expects.
https://devforums.apple.com/message/1035180#1035180
This means that you should not even try to compare closures for equality because optimizations may affect the outcome.
answered Sep 5 '14 at 21:15
![](https://i.stack.imgur.com/HPTNs.jpg?s=32&g=1)
![](https://i.stack.imgur.com/HPTNs.jpg?s=32&g=1)
drewagdrewag
73.9k25125122
73.9k25125122
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
add a comment |
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
17
17
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place...
– Zack Morris
Jan 20 '16 at 23:12
3
3
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
@ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure.
– drewag
Jan 21 '16 at 5:36
4
4
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
@drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break!
– CommaToast
Jul 24 '17 at 9:21
add a comment |
Simplest way is designate the block type as @objc_block
, and now you can cast it to an AnyObject which is comparable with ===
. Example:
typealias Ftype = @objc_block (s:String) -> ()
let f : Ftype = {
ss in
println(ss)
}
let ff : Ftype = {
sss in
println(sss)
}
let obj1 = unsafeBitCast(f, AnyObject.self)
let obj2 = unsafeBitCast(ff, AnyObject.self)
let obj3 = unsafeBitCast(f, AnyObject.self)
println(obj1 === obj2) // false
println(obj1 === obj3) // true
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
add a comment |
Simplest way is designate the block type as @objc_block
, and now you can cast it to an AnyObject which is comparable with ===
. Example:
typealias Ftype = @objc_block (s:String) -> ()
let f : Ftype = {
ss in
println(ss)
}
let ff : Ftype = {
sss in
println(sss)
}
let obj1 = unsafeBitCast(f, AnyObject.self)
let obj2 = unsafeBitCast(ff, AnyObject.self)
let obj3 = unsafeBitCast(f, AnyObject.self)
println(obj1 === obj2) // false
println(obj1 === obj3) // true
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
add a comment |
Simplest way is designate the block type as @objc_block
, and now you can cast it to an AnyObject which is comparable with ===
. Example:
typealias Ftype = @objc_block (s:String) -> ()
let f : Ftype = {
ss in
println(ss)
}
let ff : Ftype = {
sss in
println(sss)
}
let obj1 = unsafeBitCast(f, AnyObject.self)
let obj2 = unsafeBitCast(ff, AnyObject.self)
let obj3 = unsafeBitCast(f, AnyObject.self)
println(obj1 === obj2) // false
println(obj1 === obj3) // true
Simplest way is designate the block type as @objc_block
, and now you can cast it to an AnyObject which is comparable with ===
. Example:
typealias Ftype = @objc_block (s:String) -> ()
let f : Ftype = {
ss in
println(ss)
}
let ff : Ftype = {
sss in
println(sss)
}
let obj1 = unsafeBitCast(f, AnyObject.self)
let obj2 = unsafeBitCast(ff, AnyObject.self)
let obj3 = unsafeBitCast(f, AnyObject.self)
println(obj1 === obj2) // false
println(obj1 === obj3) // true
answered Aug 31 '14 at 17:02
mattmatt
332k46543741
332k46543741
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
add a comment |
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers.
– freezing_
Jan 28 '15 at 21:19
2
2
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
Use @convention(block) instead of @objc_block on Swift 2.x. Great answer!
– Gabriel.Massana
Jun 15 '16 at 10:56
add a comment |
I've been looking for the answer, too. And I've found it at last.
- https://gist.github.com/dankogai/b03319ce427544beb5a4
What you need is the actual function pointer and its context hidden in the function object.
func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
typealias IntInt = (Int, Int)
let (hi, lo) = unsafeBitCast(f, IntInt.self)
let offset = sizeof(Int) == 8 ? 16 : 12
let ptr = UnsafePointer<Int>(lo+offset)
return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
return tl.0 == tr.0 && tl.1 == tr.1
}
And here is the demo:
// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f; println("(f === g) == (f === g)")
f = genericId; println("(f === g) == (f === g)")
f = g; println("(f === g) == (f === g)")
// closures
func mkcounter()->()->Int {
var count = 0;
return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == (peekFunc(c0))")
println("peekFunc(c1) == (peekFunc(c1))")
println("peekFunc(c2) == (peekFunc(c2))")
println("(c0() == c1()) == (c0() == c1())") // true : both are called once
println("(c0() == c2()) == (c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == (c0 === c1)")
println("(c0 === c2) == (c0 === c2)")
See the URLs below to find why and how it works:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
As you see it is capable of checking identity only (the 2nd test yields false
). But that should be good enough.
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
add a comment |
I've been looking for the answer, too. And I've found it at last.
- https://gist.github.com/dankogai/b03319ce427544beb5a4
What you need is the actual function pointer and its context hidden in the function object.
func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
typealias IntInt = (Int, Int)
let (hi, lo) = unsafeBitCast(f, IntInt.self)
let offset = sizeof(Int) == 8 ? 16 : 12
let ptr = UnsafePointer<Int>(lo+offset)
return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
return tl.0 == tr.0 && tl.1 == tr.1
}
And here is the demo:
// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f; println("(f === g) == (f === g)")
f = genericId; println("(f === g) == (f === g)")
f = g; println("(f === g) == (f === g)")
// closures
func mkcounter()->()->Int {
var count = 0;
return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == (peekFunc(c0))")
println("peekFunc(c1) == (peekFunc(c1))")
println("peekFunc(c2) == (peekFunc(c2))")
println("(c0() == c1()) == (c0() == c1())") // true : both are called once
println("(c0() == c2()) == (c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == (c0 === c1)")
println("(c0 === c2) == (c0 === c2)")
See the URLs below to find why and how it works:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
As you see it is capable of checking identity only (the 2nd test yields false
). But that should be good enough.
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
add a comment |
I've been looking for the answer, too. And I've found it at last.
- https://gist.github.com/dankogai/b03319ce427544beb5a4
What you need is the actual function pointer and its context hidden in the function object.
func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
typealias IntInt = (Int, Int)
let (hi, lo) = unsafeBitCast(f, IntInt.self)
let offset = sizeof(Int) == 8 ? 16 : 12
let ptr = UnsafePointer<Int>(lo+offset)
return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
return tl.0 == tr.0 && tl.1 == tr.1
}
And here is the demo:
// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f; println("(f === g) == (f === g)")
f = genericId; println("(f === g) == (f === g)")
f = g; println("(f === g) == (f === g)")
// closures
func mkcounter()->()->Int {
var count = 0;
return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == (peekFunc(c0))")
println("peekFunc(c1) == (peekFunc(c1))")
println("peekFunc(c2) == (peekFunc(c2))")
println("(c0() == c1()) == (c0() == c1())") // true : both are called once
println("(c0() == c2()) == (c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == (c0 === c1)")
println("(c0 === c2) == (c0 === c2)")
See the URLs below to find why and how it works:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
As you see it is capable of checking identity only (the 2nd test yields false
). But that should be good enough.
I've been looking for the answer, too. And I've found it at last.
- https://gist.github.com/dankogai/b03319ce427544beb5a4
What you need is the actual function pointer and its context hidden in the function object.
func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
typealias IntInt = (Int, Int)
let (hi, lo) = unsafeBitCast(f, IntInt.self)
let offset = sizeof(Int) == 8 ? 16 : 12
let ptr = UnsafePointer<Int>(lo+offset)
return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
return tl.0 == tr.0 && tl.1 == tr.1
}
And here is the demo:
// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f; println("(f === g) == (f === g)")
f = genericId; println("(f === g) == (f === g)")
f = g; println("(f === g) == (f === g)")
// closures
func mkcounter()->()->Int {
var count = 0;
return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == (peekFunc(c0))")
println("peekFunc(c1) == (peekFunc(c1))")
println("peekFunc(c2) == (peekFunc(c2))")
println("(c0() == c1()) == (c0() == c1())") // true : both are called once
println("(c0() == c2()) == (c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == (c0 === c1)")
println("(c0 === c2) == (c0 === c2)")
See the URLs below to find why and how it works:
- https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
- https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
As you see it is capable of checking identity only (the 2nd test yields false
). But that should be good enough.
edited Aug 4 '14 at 19:55
answered Jul 28 '14 at 17:40
dankogaidankogai
1,49997
1,49997
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
add a comment |
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
3
3
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
This method will not be reliable with compiler optimizations devforums.apple.com/message/1035180#1035180
– drewag
Sep 5 '14 at 21:15
5
5
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result.
– Eonil
Nov 9 '14 at 20:10
6
6
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code.
– Cristik
Jan 23 '16 at 21:29
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language intentionally doesn't have function equality, for the exact purpose of freeing the compiler to break function equality freely in order to yield better optimizations.
– Alexander
Jan 3 at 4:44
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
... and this is exactly the approach Chris Lattner advocates against (see the top answer).
– pipacs
Mar 6 at 14:45
add a comment |
I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
add a comment |
I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
add a comment |
I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
answered Aug 17 '15 at 12:12
tuncaytuncay
6111
6111
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
add a comment |
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
2
2
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility.
– Barry
Jul 3 '16 at 5:22
add a comment |
This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast
examples, most of them don't show the full picture, here's a more detailed one:
typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()
func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
let objA = unsafeBitCast(a, AnyObject.self)
let objB = unsafeBitCast(b, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testAnyBlock(a: Any?, _ b: Any?) -> String {
if !(a is ObjBlock) || !(b is ObjBlock) {
return "a nor b are ObjBlock, they are not equal"
}
let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
class Foo
{
lazy var swfBlock: ObjBlock = self.swf
func swf() { print("swf") }
@objc func obj() { print("obj") }
}
let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()
print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.
I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block)
on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you haveStruct S { func f(_: Int) -> Bool }
, you actually have a function of typeS.f
which has type(S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding theself
parameter by calling the method on an object, e.g.S().f
, or by explicitly binding it, e.g.S.f(S())
), you create a new closure object. This object stores a pointer toS.f
(which can be shared), but also to your instance (
self, the
S()`).
– Alexander
Jan 3 at 4:41
This closure object must be unique per instance ofS
. If closure pointer equality were possible, then you would be surprised to discover thats1.f
is not the same pointer ass2.f
(because one is a closure object which referencess1
andf
, and the other is a closure object which referencess2
andf
).
– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
add a comment |
This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast
examples, most of them don't show the full picture, here's a more detailed one:
typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()
func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
let objA = unsafeBitCast(a, AnyObject.self)
let objB = unsafeBitCast(b, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testAnyBlock(a: Any?, _ b: Any?) -> String {
if !(a is ObjBlock) || !(b is ObjBlock) {
return "a nor b are ObjBlock, they are not equal"
}
let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
class Foo
{
lazy var swfBlock: ObjBlock = self.swf
func swf() { print("swf") }
@objc func obj() { print("obj") }
}
let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()
print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.
I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block)
on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you haveStruct S { func f(_: Int) -> Bool }
, you actually have a function of typeS.f
which has type(S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding theself
parameter by calling the method on an object, e.g.S().f
, or by explicitly binding it, e.g.S.f(S())
), you create a new closure object. This object stores a pointer toS.f
(which can be shared), but also to your instance (
self, the
S()`).
– Alexander
Jan 3 at 4:41
This closure object must be unique per instance ofS
. If closure pointer equality were possible, then you would be surprised to discover thats1.f
is not the same pointer ass2.f
(because one is a closure object which referencess1
andf
, and the other is a closure object which referencess2
andf
).
– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
add a comment |
This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast
examples, most of them don't show the full picture, here's a more detailed one:
typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()
func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
let objA = unsafeBitCast(a, AnyObject.self)
let objB = unsafeBitCast(b, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testAnyBlock(a: Any?, _ b: Any?) -> String {
if !(a is ObjBlock) || !(b is ObjBlock) {
return "a nor b are ObjBlock, they are not equal"
}
let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
class Foo
{
lazy var swfBlock: ObjBlock = self.swf
func swf() { print("swf") }
@objc func obj() { print("obj") }
}
let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()
print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.
I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block)
on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?
This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast
examples, most of them don't show the full picture, here's a more detailed one:
typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()
func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
let objA = unsafeBitCast(a, AnyObject.self)
let objB = unsafeBitCast(b, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
func testAnyBlock(a: Any?, _ b: Any?) -> String {
if !(a is ObjBlock) || !(b is ObjBlock) {
return "a nor b are ObjBlock, they are not equal"
}
let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
return "a is ObjBlock: (a is ObjBlock), b is ObjBlock: (b is ObjBlock), objA === objB: (objA === objB)"
}
class Foo
{
lazy var swfBlock: ObjBlock = self.swf
func swf() { print("swf") }
@objc func obj() { print("obj") }
}
let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()
print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true
The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.
I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block)
on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?
edited Apr 13 '16 at 19:38
answered Feb 3 '16 at 14:45
Ian BytchekIan Bytchek
6,10552857
6,10552857
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you haveStruct S { func f(_: Int) -> Bool }
, you actually have a function of typeS.f
which has type(S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding theself
parameter by calling the method on an object, e.g.S().f
, or by explicitly binding it, e.g.S.f(S())
), you create a new closure object. This object stores a pointer toS.f
(which can be shared), but also to your instance (
self, the
S()`).
– Alexander
Jan 3 at 4:41
This closure object must be unique per instance ofS
. If closure pointer equality were possible, then you would be surprised to discover thats1.f
is not the same pointer ass2.f
(because one is a closure object which referencess1
andf
, and the other is a closure object which referencess2
andf
).
– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
add a comment |
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you haveStruct S { func f(_: Int) -> Bool }
, you actually have a function of typeS.f
which has type(S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding theself
parameter by calling the method on an object, e.g.S().f
, or by explicitly binding it, e.g.S.f(S())
), you create a new closure object. This object stores a pointer toS.f
(which can be shared), but also to your instance (
self, the
S()`).
– Alexander
Jan 3 at 4:41
This closure object must be unique per instance ofS
. If closure pointer equality were possible, then you would be surprised to discover thats1.f
is not the same pointer ass2.f
(because one is a closure object which referencess1
andf
, and the other is a closure object which referencess2
andf
).
– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication
– Alexander
Jan 3 at 4:33
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer can't be, because there's one closure object per set of enclosed values.
– Alexander
Jan 3 at 4:37
So when you have
Struct S { func f(_: Int) -> Bool }
, you actually have a function of type S.f
which has type (S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding the self
parameter by calling the method on an object, e.g. S().f
, or by explicitly binding it, e.g. S.f(S())
), you create a new closure object. This object stores a pointer to S.f
(which can be shared), but also to your instance (
self, the
S()`).– Alexander
Jan 3 at 4:41
So when you have
Struct S { func f(_: Int) -> Bool }
, you actually have a function of type S.f
which has type (S) -> (Int) -> Bool
. This function can be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding the self
parameter by calling the method on an object, e.g. S().f
, or by explicitly binding it, e.g. S.f(S())
), you create a new closure object. This object stores a pointer to S.f
(which can be shared), but also to your instance (
self, the
S()`).– Alexander
Jan 3 at 4:41
This closure object must be unique per instance of
S
. If closure pointer equality were possible, then you would be surprised to discover that s1.f
is not the same pointer as s2.f
(because one is a closure object which references s1
and f
, and the other is a closure object which references s2
and f
).– Alexander
Jan 3 at 4:42
This closure object must be unique per instance of
S
. If closure pointer equality were possible, then you would be surprised to discover that s1.f
is not the same pointer as s2.f
(because one is a closure object which references s1
and f
, and the other is a closure object which references s2
and f
).– Alexander
Jan 3 at 4:42
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! 👍
– Ian Bytchek
Jan 3 at 7:51
add a comment |
Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):
Swift:
typealias Callback = (Any...)->Void
class Command {
init(_ fn: @escaping Callback) {
self.fn_ = fn
}
var exec : (_ args: Any...)->Void {
get {
return fn_
}
}
var fn_ :Callback
}
let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
print(args.count)
}
cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")
cmd1 === cmd2 // true
cmd1 === cmd3 // false
Java:
interface Command {
void exec(Object... args);
}
Command cmd1 = new Command() {
public void exec(Object... args) [
// do something
}
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
public void exec(Object... args) {
// do something else
}
}
cmd1 == cmd2 // true
cmd1 == cmd3 // false
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
add a comment |
Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):
Swift:
typealias Callback = (Any...)->Void
class Command {
init(_ fn: @escaping Callback) {
self.fn_ = fn
}
var exec : (_ args: Any...)->Void {
get {
return fn_
}
}
var fn_ :Callback
}
let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
print(args.count)
}
cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")
cmd1 === cmd2 // true
cmd1 === cmd3 // false
Java:
interface Command {
void exec(Object... args);
}
Command cmd1 = new Command() {
public void exec(Object... args) [
// do something
}
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
public void exec(Object... args) {
// do something else
}
}
cmd1 == cmd2 // true
cmd1 == cmd3 // false
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
add a comment |
Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):
Swift:
typealias Callback = (Any...)->Void
class Command {
init(_ fn: @escaping Callback) {
self.fn_ = fn
}
var exec : (_ args: Any...)->Void {
get {
return fn_
}
}
var fn_ :Callback
}
let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
print(args.count)
}
cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")
cmd1 === cmd2 // true
cmd1 === cmd3 // false
Java:
interface Command {
void exec(Object... args);
}
Command cmd1 = new Command() {
public void exec(Object... args) [
// do something
}
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
public void exec(Object... args) {
// do something else
}
}
cmd1 == cmd2 // true
cmd1 == cmd3 // false
Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):
Swift:
typealias Callback = (Any...)->Void
class Command {
init(_ fn: @escaping Callback) {
self.fn_ = fn
}
var exec : (_ args: Any...)->Void {
get {
return fn_
}
}
var fn_ :Callback
}
let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
print(args.count)
}
cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")
cmd1 === cmd2 // true
cmd1 === cmd3 // false
Java:
interface Command {
void exec(Object... args);
}
Command cmd1 = new Command() {
public void exec(Object... args) [
// do something
}
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
public void exec(Object... args) {
// do something else
}
}
cmd1 == cmd2 // true
cmd1 == cmd3 // false
edited Mar 17 '17 at 12:12
answered Mar 16 '17 at 16:37
![](https://i.stack.imgur.com/VxDDo.jpg?s=32&g=1)
![](https://i.stack.imgur.com/VxDDo.jpg?s=32&g=1)
basobaso
313
313
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
add a comment |
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
This would be much better if you made it Generic.
– Alexander
Jan 3 at 4:28
add a comment |
Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:
As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self
):
But – and this is just an idea – I can't help but notice that the where
clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?
add a comment |
Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:
As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self
):
But – and this is just an idea – I can't help but notice that the where
clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?
add a comment |
Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:
As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self
):
But – and this is just an idea – I can't help but notice that the where
clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?
Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:
As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self
):
But – and this is just an idea – I can't help but notice that the where
clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?
answered Jun 11 '14 at 14:54
JiaaroJiaaro
46k32135170
46k32135170
add a comment |
add a comment |
Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).
So something like this:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
Now you just need to store the key
returned by the "register" function and pass it when unregistering.
add a comment |
Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).
So something like this:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
Now you just need to store the key
returned by the "register" function and pass it when unregistering.
add a comment |
Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).
So something like this:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
Now you just need to store the key
returned by the "register" function and pass it when unregistering.
Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).
So something like this:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
Now you just need to store the key
returned by the "register" function and pass it when unregistering.
edited Jan 7 at 23:14
answered Jan 7 at 23:03
vir usvir us
3,35412735
3,35412735
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f24111984%2fhow-do-you-test-functions-and-closures-for-equality%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2YbAfXS,GcPYSH4WwKFQr51fJzrF7zUIg,rm8g4YSx9,Z1MVMsutlabx5 I
5
As far as I can tell, you also can't check equality of metaclasses (e.g.,
MyClass.self
)– Jiaaro
Jun 9 '14 at 0:55
It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution.
– Bill
Jun 9 '14 at 12:08
1
Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.
– Jessy
Jun 9 '14 at 15:42
2
Great question, but totally separate thing: your use of a diacritic on
å
to referencea
is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.)– Rob Napier
Jun 11 '14 at 12:50
2
@Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design.
– Zack Morris
Jan 20 '16 at 22:50