The Lava callback concept

Presently there does not exist a common understanding on how to support event/signal notification (callbacks) in modern object-oriented programming languages.

In the course of the Java development Sun Microsystems has replaced the original event handling concept by a new philosophy based on event listeners, adapter classes, and Java language extensions introducing anonymous and inner classes.

Microsoft has made (and withdrawn) a proposal for an own callback-specific Java language extension ("delegates"), and has then provided a more comprehensive delegate or callable entity notion in C#.

The problem is primarily to find a truly object-oriented and type-safe way to associate arbitrary "client data", provided by the envisaged callback receiver, with the event/signal sender object ("server") without requiring the signal sender to "know" the type and structure of the client data.

We felt that the present Java and C# solutions are too complex and indirect and lack the desirable intuitive simplicity and clarity. In contrast to the former, the Lava solution doesn't require the introduction of additional types of entities (like delegates, closures, or inner classes) but is solely based on three specific executable statements: connect, disconnect and signal.

The principal idea has been taken over from the signal/slot concept of the Qt toolkit, and has been modified and adapted to the conceptual setting of the Lava language:

In the signal sender class a signal (that can be emitted by an object of this class) is declared as a member function without output parameters, having the special "signal" attribute. A signal function doesn't have an implementation. An object of a class having signals may explicitly emit a signal using the signal statement (instead of a normal function call statement), with actual parameters, say parm1, parm2, ... :

signal self.sigFunc (parm1,parm2,...)

Note that an object (self in the example) can only itself emit a signal; it cannot force another object to emit a signal, except, of course, if the class of the other object exports a method having this effect (like the sendSignal function in our callback sample).

You can use the connect statement to specify to which handler objects and handler methods a signal is to be transmitted:

connect sigSender.sigFunc to handlerObj.hdlFunc

You may connect the same signal sender / signal function to several handler objects / handler functions. When the sender emits this signal, then all handlers will be invoked in the order of the corresponding connects

Transmitting a signal to a handler object handlerObj having a handler method hdlFunc means that the handler function is invoked with that handler object as call object, and the actual parameters are taken from the signal statement, just as if the following function call would be performed in place of the signal function call:

call handlerObj.hdlFunc(parm1,parm2,...)

This requires that the handler function must have the same number of input (and no output) parameters, and the formal parameters of the signal function must be assignment compatible to the corresponding formal parameters of the handler function.

Moreover, if the call objects or the formal parameters of the functions occurring in the connect statement depend on virtual types, then LavaPE will reject the connect if sigSender and handlerObj don't belong to the same pattern context and thus don't apply the same interpretation to the involved virtual types.

The envisaged extension of the Lava GUI support requires that you can also specify a signal sender class only in the connect statement, rather than a concrete signal sender object (to be explained after the GUI support extension.)


Alternative approaches:

The "X Toolkit" of the "X Window System" has provided an early approach to callback programming. Its XtAddCallback function can be viewed as an ancestor of our connect construct used for registering a new callback.

The traditional Java approach uses event listener interfaces and associated adapter classes. Lava's signal/connect approach doesn't need such additional auxiliary classes.

From a more abstract point of view, speaking in terms of design patterns (cf. also the pattern support in Lava), callbacks can be viewed as examples of the observer pattern.

In recent years closures have become more and more popular as a means to cope with callbacks, and several programming languages provide closures now or are planning closure enhancements. See for instance this proposal for Java closures, and this article, which compares three proposals for closures in Java. Mark Reinhold's Blog on the Java closure debate puts more emphasis on the relevance of closures for parallel programming and assesses the previous proposals as not "entirely appropriate".

In contrast to the present closure enthusiasm, we feel that the closure notion is quite contrary to our Lava philosophy. In our opinion closures tend to obscure the data flow as well as the control flow of programs by implicitly passing data around and by involving one or another kind of function pointers. In contrast to this, Lava prefers explicit data flow and explicit control flow, both running strictly "from top to bottom" and being amenable to static analysis algorithms ("at programming time"). Although the implementation of the signal/connect constructs of Lava involves some kind of internal function pointers, these aren't exposed to the programmer, however, and the data flow between the callback requester, signal emitter and signal handler objects is expressed quite explicitly by specifying these objects and by using the parameters of the signal/handler functions.


See also our callback sample program.