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:

public event EventHandler MyEvent;

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:

if (MyEvent != null)
{ MyEvent(this, EventArgs.Empty); }

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:

myInstance.MyEvent += this.myInstance_MyEvent;
myInstance.MyEvent -= this.myInstance_MyEvent;

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:

EventHandler myHandler = delegate(object sender, EventArgs e) { Console.WriteLine("MyEvent triggered from " + sender); };

myInstance.MyEvent += myHandler;
myInstance.MyEvent -= myHandler;

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:

class Event[T]() {

  private var invocationList : List[T => Unit] = Nil

  def apply(args : T) {
    for (val invoker <- invocationList) {
      invoker(args)
    }
  }

  def +=(invoker : T => Unit) {
    invocationList = invoker :: invocationList
  }

  def -=(invoker : T => Unit) {
    invocationList = invocationList filter ((x : T => Unit) => (x != invoker))
  }

}

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:

val myEvent = new Event[Int]()

val functionValue = ((x : Int) => println("Function value called with " + x))
myEvent += functionValue
myEvent(4)
myEvent -= functionValue
myEvent(5)

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:

def method(x : Int) = println("Class method called with " + x)

And we then try and do something like this:

myEvent += method
myEvent(2)
myEvent -= method
myEvent(3)

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:

val memberFunctionValue = (x : Int) => {
  println("Member function value called with " + x)
}

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:

var _foo : Int

def foo() = _foo

def foo_=(value : Int) {
  _foo = value
}

I tried to define a class like this:

class MyClass {

    private val _myEvent = new Event[Int]()

    public def myEvent_+=(function : Int => Unit) {
        _myEvent += function
    }

    public def myEvent_-=(function : Int => Unit) {
        _myEvent -= function
    }

    public def doSomethingCausingMyEventToRaise() {
        // ...
        _myEvent(1)
        // ...
    }

}

This encapsulates the event, allowing us to control exactly who can invoke it. Unfortunately, code of the form:

val myInstance = new MyClass
myInstance.myEvent += method
myInstance.doSomethingCausingMyEventToRaise
myInstance.myEvent -= method

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...