Skip to content

Instantly share code, notes, and snippets.

@purplefox
Created March 26, 2012 10:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save purplefox/2204406 to your computer and use it in GitHub Desktop.
Save purplefox/2204406 to your computer and use it in GitHub Desktop.
I need a reference so I can unregister a handler from within a handler. otherwise would need to declare the handler first, put it in a variable and use that.
Wanna be able to do this with anon handlers
foo.registerHandler(myClosure = {
// Do something
// Now unregister handler
foo.unregisterHandler(myClosure)
})
@purplefox
Copy link
Author

BTW... this is similar to how it's done in JavaScript, which makes a nice compact idiom:

foo.registerHandler(function myHandler() {
// Do something
// Now unregister handler
foo.unregisterHandler(myHandler);
});

@pledbrook
Copy link

Not doable. You would have to pass in the reference to itself as an argument to the closure - or overload 'registerHandler()' to wrap the call and do the deregistering itself. But I guess that's not what you want!

@glaforge
Copy link

class Foo {
    void registerHandler(Closure c) {
        println "registering... ${c.toString()}"
        c.self = c
        c()
    }

    void unregisterHandler(self) {
        println "unregistering... ${self.toString()}"
    }
}

def foo = new Foo()

foo.registerHandler {
   // Do something
   // Now unregister handler
   foo.unregisterHandler(self)
}

@glaforge
Copy link

Another approach could be to use named arguments:

foo.registerHandler myHandler: {
    // do something
    foo.unregisterHandler myHandler
}

You'd inject a myHandler in the closure attributes that would be named myHandler and referencing that closure.

@glaforge
Copy link

Yet another approach could also be to have a special unregister() method added to the closure that would do the job of unregistering itself.
It depends also on how those handlers are to be used, if they can be used multiple times by multiple Foo, etc.

@purplefox
Copy link
Author

I tried the named arguments approach:

w.register myHandler: {
w.unregister(myHandler)
}

But it fails with:

tim@Ethel:~/projects/vert.x$ groovy src/main/groovy/org/vertx/groovy/core/scratch.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: org.vertx.groovy.core.Wibble.register() is applicable for argument types: (java.util.LinkedHashMap) values: [[myHandler:org.vertx.groovy.core.scratch$_run_closure1@66e43eb8]]
Possible solutions: register(groovy.lang.Closure), unregister(groovy.lang.Closure)
groovy.lang.MissingMethodException: No signature of method: org.vertx.groovy.core.Wibble.register() is applicable for argument types: (java.util.LinkedHashMap) values: [[myHandler:org.vertx.groovy.core.scratch$_run_closure1@66e43eb8]]
Possible solutions: register(groovy.lang.Closure), unregister(groovy.lang.Closure)
at org.vertx.groovy.core.scratch.run(scratch.groovy:37)

It seems to be interpreting the argument to register as a map literal (?)

@pledbrook
Copy link

To handle named arguments, you need to have a signature of the form:

void register(Map args) {
    ...
}

In fact, to get the above behaviour, the implementation would have to be something like:

void register(Map args) {
    def c = args.myHandler.clone()
    c.delegate = args
    c.call(...)
}

I don't think this is a sensible approach because it introduces some subtleties of behaviour that might confuse users. I think the approach that injects a 'self' argument into the closure is better.

@glaforge
Copy link

class Foo {
    void registerHandler(Map m) {
        println "registering..."
        def handlerName = m.keySet().iterator()[0]
        def closure = m[handlerName]
        closure.setProperty(handlerName, closure)
        closure()
    }

    void unregisterHandler(Closure handler) {
        println "unregistering..."
    }
}

def foo = new Foo()

foo.registerHandler myHandler: {
   // Do something
   // Now unregister handler
   foo.unregisterHandler(myHandler)

}

@purplefox
Copy link
Author

Well... I just tested my original suggestion:

foo.registerHandler(myClosure = {
// Do something
// Now unregister handler
foo.unregisterHandler(myClosure)
})

And it seems to work fine :)

I logged out the object being passed to register and unregister and it's the same instance.

@glaforge
Copy link

Well, it works in some circumstances but not all.
Here, you're in a Script, so it's going to add a value in the binding.
But within a class, this mechanism wouldn't work.
So it's not a very good idea to rely on that.

@pledbrook
Copy link

@glaforge: Is that even supported syntax? In other words, is it supposed to work like that, or is it just a coincidence that it works and the behaviour may change in the future?

@glaforge
Copy link

@pledbrook: it's like in while(line = readLine()) or in def a = b = 2, etc.
Although it's not necessarily something advised to do!

@purplefox
Copy link
Author

IMHO it's really useful. This is exactly what I wanted Groovy to support....

Being able to unregister handlers from inside that handler is something you do all the time in event driven frameworks. Without something like this it becomes difficult.

@glaforge
Copy link

@purplefox: understood yes. Although the trick with named arguments I showed earlier is IMHO a better and cleaner approach (works in classes, you're in control, familiar Groovy syntax, etc).

@purplefox
Copy link
Author

imho injecting a "self" reference into the closure would be simpler.

For named parameters, user won't always want to provide names and there can several other params too. Adding the param passing code in many places in vert.x (there are lots of places that take handlers) would be a pita.

Injecting self is still quite demanding, although easier. Just a thought: Any reason why Groovy doesn't inject a "self" (or set this) to point to the closure by default. I would have thought this would be a pretty useful thing.

@glaforge
Copy link

Well, a long time ago, "this" pointed at the closure itself, and not at the surrounding class. But we changed that so that "this" had the same meaning as when you're inside any other Java block (think for example of for loops vs each loops).
And then we didn't introduce a "self", as it's only really needed when you do recursive algorithms with closures, which is not necessarily something you do that often in practice, and the workaround of first declaring the closure seemed to not have been too painful.
But which doesn't mean we can't or won't introduce that at some point.

@pledbrook
Copy link

getSelf() on Closure for 2.0 would get my vote, but it does probably constitute a breaking change and undoubtedly there would be confusion between 'this' and 'self'. Anyway, this is a digression, sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment