C#-Style Events In Scala
As you may know, C# has a rather lovely little feature whereby you can setup an event on a class, like so:
Where EventHandler
is a delegate (i.e. function pointer) type. The built-in EventHandler
delegate just has the signature (Object, EventArgs) -> Void
, so from within the class we can invoke the event like so:
And this will cause any anonymous delegates or methods which have been attached to the event to be invoked with those arguments. This attachment and detachment is done like so:
Where we have created a method on the subscribing instance called myInstance_MyEvent
with the appropriate signature. We could also have supplied an anonymous delegate to the event, but would need to keep a reference to it around in order to remove it later:
Right, so far so ordinary. As part of my ongoing quest to reimplement C# in Scala (see also here) , I’ll try and code up an equivalent system in Scala, which does not have a built in event system. The approach I took was to define a class, Event:
This is a pretty simple bit of code. It just defines two methods with the same names as the operators of C# which add or remove the functions they receive as arguments to and from an invocation list, and a method with the special name “apply” which will be invoked when the event is supplied with an argument list by the usual juxtaposition convention. Now, let’s take a look at some example code that uses this:
This will simply print “Anonymous function called with 4”. Great! Looks like a very direct equivalent of C#’s mechanism. But alas, there is a bit of a subtlety. What about if we have a method such as this:
And we then try and do something like this:
What you actually find is that you get notification on the console of both the 2 and the 3 that were passed to the event! Why is this? Well, if you look at the generated code in a Java decompiler you will find that actually Scala internally creates a class to coerce the class method into its internal representation of a function. The problem is that his coercion is done separately for both usages of “method”, which leads to two function objects which are not reference equal and hence the invocation list never has its item removed!
Sidenote: actually, interestingly the Scala compiler creates entirely different CLASSES for each coercion, even though these classes have identical functionality!
The only sensible way this code could be fixed is for a more intuitive implementation of equality to be given to the adapter classes generated in this way (obviously we cannot compare functions for equality in general!).
Happily, there is a workaround, but it smells a bit. Instead of declaring a method like that, declare it like this:
This creates a member function value instead of a method, which looks the same for its callers from Scala code (but not from Java). This means that reference equality does the right thing when trying to remove the “method” from the event.
Another irritating limitation of the approach is that anyone is allowed to raise the event. This is in contrast to C#, where only the class hierarchy that declared it has such access: a much better design principle. On the basis that Scala supports syntax such as the following for property access:
I tried to define a class like this:
This encapsulates the event, allowing us to control exactly who can invoke it. Unfortunately, code of the form:
Does not compile: the myEvent_
style method names I added to the class are simply not used, and that code instead tries to find a (non existent) field called myEvent
in MyClass
. Of course, calling the myEvent_
methods of MyClass
directly does work, but we lose the nice feature of the syntax whereby events feel like they are built into the language.
So overall things didn’t work out great for replicating C# events in Scala, but it was still an interesting voyage of discovery. Furthermore, if we could convince the language designers to support desugaring for arbitrary methods of the form “foo_[some symbols]” rather than just the special case of properties we could begin to do some rather interesting things…