Arnt Gulbrandsen
About meAbout this blog

Morbid bindings

The first thing I learned about programming in-person (not from a book or from code) was morbid bindings, a strangely unused term.

I learned it from a university employee called Eric Monteiro, who was oddly clueful in a generally sparsely beclued environment. Can't remember his title, or what he taught.

I had just listened to Eric answer someone else about multiple inheritance, and asked a followup question, which he answered, and then he digressed into relationships in general: When two things are connected in a program, the binding is always one of three: A kind, a belonging, or else it's morbid, and morbid bindings always turn out to be bad in one way or another. He gave me a quick example: This number in this file has to be at the same as that number in that file and I think I answered, such as maximum line length in two functions that read/write the same file?

Kind: This one's well-known. In java a List is-a Collection, which ties the List to the Collection because it's a kind of Collection. An object is an instance of a class (in most OO languages), which ties the object to the class in a similar way. Some of these can be argued. Is an integer a number? Mostly I would say yes, even though a+1>a is true for numbers but not necessarily for integers in programming languages.

Belonging: A java member function belongs to its class. (It also is an instance of another class.) A member variable generally belongs to an object/instance, or sometimes to a class. A server belongs to its client(s) (or in some designs the other way around). In many cluster designs, nodes belong to the cluster. The keys in a hash map belong to the hash map (NB: to the instance, not to the class). A unit test belongs to the code it tests. A called function belongs to the calling function for the duration of the function call.

Morbid bindings: The rest, by definition. I'll give three examples and argue that turning the morbid binding into something else makes the program better in each case.

These have to be the same. Locking order is often a matter of morbid binding, but does not have to be. There can be a definition of truth and each locking regions can belong to it.

For example, the locking API could enforce the locking order, or a static analysis tool could check the locking regions and block those that cannot be verified. In these cases each locking region has a belonging relationship with either the locking API or the static analysis tool, and because of that the binding between two locking regions is not morbid.

When there is no such relationship, then each code site that locks is morbidly bound to the others.

These have to be different. String is a ruby system class and rails adds new functions to it, such as pluralize and singularize, a practice known as monkeypatching. If several classes monkeypatch e.g. String, they are morbidly bound to each other, because their sets of extensions cannot overlap. By using e.g. static analysis to avoid monkeypatching conflicts, one binds the monkeypatchers to the analyser, and coincidentally improves the program by making each subsystem's unit test test the actual program. (Quite often each subsystem sees only its own monkeypatches because that's the last subsystem initialised before the test runs, so the tests show 100% green but in reality there's subtle breakage.)

X' input and Y's input/output must match. Sometimes two functions have to cover all the same cases, e.g. to allow equivalent input from two sources, or to ensure input/output correspondence.

Java's Serializable interface requires the caller to implement two functions, one to read and one to write the object. Nothing in java ensures that the reader can read everything the writer can write, therefore your readObject() and your writeObject() are morbidly bound to each other unless you take steps to prevent that. You could, for example, write writeObject() in such a way that you can also write a script that converts your writeObject() source into unit tests for readObject(). The act of binding readObject() to writeObject() via its tests converts a morbid binding into an indirect belonging and also ensures that your class really is Serializable.