Annotations

Perfection in the MarginsMany people, from various programming communities, have come to the conclusion that if functions are first-class objects they should be allowed properties. For example, if a function in a test suite is checking for regression of the fix to a particular ticket, it could have a property for the ticket number. Then other parts of the code can deal with it in a consistent way through introspection, without keeping related information all over the place.

In Java, these are known as annotations and look like this:

@ticket(177177)
public static void testFrobulation () { ...

Python also calls them annotations (replacing the older system of decorators, which looked similar to the Java syntax but achieved its effect in a way more like Lisp macros). Annotations in Python look like this:

def testFrobulation(ticket: 177177): ...

In the past few months, two infelicities of the Metacity codebase presented themselves. Firstly, some translators complained, rather reasonably, in GNOME bug 469361 that the same information was repeated over and over again in the long descriptions of the keybindings.  Secondly, the same information about keybindings was repeated over and over in several places in the code, and in many of those places it was required to stay in the same order as the other places and would cause subtle breakage if it didn’t.  And of course, when anyone provided a patch to add a new keybinding it needed to modify several more files than would seem really necessary.  What would have been best is to be able to write something like

static void @keybinding ("Activate the window menu", "<Alt>Space")
handle_activate_window_menu (MetaDisplay    *display, ...

But you can’t really do annotations in C (despite a half-arsed attempt by Microsoft). Or can you?

In Dr Dobb’s Journal of May 2001, Randy Meyers introduced an idea called x-macros. This idea is the mildest of preprocessor abuse: you create a file containing lines of grouped data, like these:

item (switch_windows, "Move between windows, using a popup window", "<Alt>Tab")
item (switch_to_workspace_1, "Switch to workspace 1", NULL)
...

Then you #include the file anywhere you want to work with this data, but you #define item() as appropriate each time.  This way, the data only needs to be modified in one place, and it’s guaranteed to stay in order.  It occurs to me that what we have here is pretty close to annotation.  True, it’s in another file, but C also requires that the prototypes of public functions be in a separate file, and nobody minds about that.

So Metacity in trunk is now working fine using this system.  There are a few places it could be neater;  screen and window bindings have always been kept separate, but it would possibly make sense to keep them together and distinguish them (as they already are distinguished) by the cunning use of flags.  Also, the handler functions should probably be given by name and not calculated from root and suffix, because it’s just confusing.

This whole idea also means that the GConf schemas which concern keybindings can be generated during the build process, which solves GNOME bug 469361 rather neatly.  In conversation with Rodney Dawes it also came up that most of the short and long versions of the keybinding descriptions say the same thing in twice in different words, which is an unfair burden on the translators.  I have merged them as appropriate: the number of strings has gone down by about thirty, and the .pot file is now 70% of its former size.

Photo: Perfection in the Margin, © Adam Shaylor, cc-by-nd.

3 Comments

  1. Robin
    Posted October 18, 2008 at 11:00 pm | Permalink

    You seem to have the wrong idea about what Python’s parameter annotations are. Parameter annotations are not meant to replace decorators, they complement them. With parameter annotations it’s possible to add information to single parameters, like a documentation string for example:

    def compile(source: “something compilable”,
    filename: “where the compilable thing comes from”,
    mode: “is this a single statement or a suite?”):

    Source, file and mode are normal parameters of the function for which the caller has to provide a value, unlike with the ticket example. For that you would use a normal function decorator:

    @ticket(177177)
    def test_frobulation(): …

  2. Posted October 19, 2008 at 10:04 pm | Permalink

    This is what I like about Metacity. It may not be full featured, but it does what it’s supposed to do and does it very well, always trying to perfect it’s architecture. When I look at other, more “flashy”, window managers, I think: yes, they’re pretty, but do they have people behind them that think about all the details of a window manager like Metacity’s devs? I really don’t think so. My only hope is that the other window managers drink from Metacity’s cup.

    Good job, guys. I really hold Openbox dearly close to my heart, but Metacity takes the gold.

  3. Posted October 20, 2008 at 3:20 am | Permalink

    @Robin: Thank you; I stand corrected.

    @Henrique: Thank you. It’s people like you that make it all worthwhile!

One Trackback

  1. By 2.25.2 released - …for the adult in you on October 22, 2008 at 5:48 pm

    [...] Changed keybindings to be in a single place (Thomas ) (GNOME bug 469361) [...]