Tuesday, March 25, 2008

 

Announcements Framework for Dolphin Smalltalk

I while ago I read an interesting article about Cincom Announcements, which pointed out that while most things in Smalltalk are an object in their own right - triggered events have traditionally only been a Symbol (possibly due to the 640k limit of the early Digitalk Smalltalk).

For me, Smalltalk has always been a language and environment that has so many good examples of how things should be done (MVC, Refactoring, Images, IDE's etc), so understanding how this was fixed was something that greatly interested me.

I frequently program in Dolphin Smalltalk in my spare time, so I was curious how the use of Announcements might change my code. I had already noticed that as my Iterex tracking application became bigger it became more awkward to keep track of what events I was generating, and I was sometimes looking for a good place to put common functionality that appeared in event handlers (I have written about some of my experiences in a later post).

With this in mind, I decided to port the Cincom VisualWorks Announcements package to Dolphin to try it out. Unfortunately my initial attempt was not completely successful - the VisualWorks implementation is surprisingly large (although a large part of this is an extensive suite of unit tests). While I was able to quickly file-in the code and get most things to compile, there was a subset of tests that I couldn't get to pass. These dealt with a language feature of Cincom that is not available in other Smalltalk's - namely ephemerons. While I thought I understood the purpose of these, I was never able to code something that would emulate them to the extent that the tests would pass.

After a few attempts, it occurred to me that I had seen an implementation in Squeak Smalltalk and so I was curious about how this had been done. It turns out that there are actually 3 or more implementations, however I hadn't realised this at the time:

  1. A minimal implementation as part of the OmniBrowser framework and described in the thread: Announcements in Omnibrowser 
  2. An extended version of the initial OmniBrowser implementation slightly generalized and separately packaged.
  3. A complete port of the original announcement framework as found in Cincom VisualWorks.

I started out with #2 above, and was quickly taken by how compact the implementation is, although I was also slightly disappointed with how small its suite of tests was (there are tests, but they are very minimal). To understand the limitations of the implementation, I decided to augment the basic tests with those provided with the Cincom implementation.

As I copied each test, I ran it, and if it failed I considered whether it was an important requirement for a basic implementation, or whether it was a more esoteric requirement that I could cover later (in that case I moved the test to a separate package). I ended up with an interesting hybrid of the two implementations.

The Squeak implementation #2 uses a global singleton announcer object whereas the VisualWorks implementation uses an event table on each object - I found that the latter is also much more natural in Dolphin, and in fact as there is already an event table for objects (either directly in the case of Model objects and their subclasses, or indirectly via an external dictionary for other objects) I was able to simply add Announcement in the same place.

I was also quite taken by both Squeak and Cincom's use of AnnouncementSet - and simply implementing concatenation on Announcement to return the composite Set:

, anAnnouncementClass
^ AnnouncementSet with: self with: anAnnouncementClass



Furthermore, I was inspired by how the Squeak implementation takes this a step further and also delegates to Announcement or AnnouncementSet to see if it #handles: a particular announcement (this is a a neat simplification of code). This fitted in nicely with the Dolphin implementation as I simply added #handles: to symbol so that it can treat both Annoucements and Symbols similarly in the original code:

triggerAnnouncement: anAnnouncement for: anObject
...
self keysAndValuesDo: [:announcementKey :actions |
(announcementKey handles: announcementToTrigger)
ifTrue: [toAnnounce addAll: actions]].

announcementToTrigger class broadcast: announcementToTrigger to: toAnnounce



It's also worth mentioning that the last line of the above method paves the way for Meta-Announcments. By delegating to the Announcement class to broadcast its event you have control on how events are sent, and in the Dolphin implementation I have added the following:

broadcast: anAnnouncementInstance to: anEventSet
super broadcast: anAnnouncementInstance to: anEventSet.

self broadcastMeta: anAnnouncementInstance




Where #broadcastMeta: allows the client to subscribe to MetaAnnouncement's and hence subscribe to all announcements for debugging purposes or simply as a more convenient way to know that events have happened:

broadcastMeta: anAnnouncementInstance
self announce: (MetaAnnouncement for: anAnnouncementInstance).
self == ObservableAnnouncement ifFalse:
[ self superclass broadcastMeta: anAnnouncementInstance].



I haven't played around with these MetaAnnouncments much (but did write some tests for them) - but certainly the debugging aspect strikes me as being particularly useful. This simple trick might be something interesting to roll into the Squeak and Cincom implementations.


Having successfully ported Announcements to Dolphin, I have found that they have simplified some of my code (a topic for a future post). I was also pleased to find that they work well with the Dolphin packaging system and I had no troubles creating my final .exe using the standard tools.


The final Dolphin packages can be downloaded from the Iterex Dolphin Announcements page.

Labels: , ,






<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]