Avoiding excess redundancy

There is an interesting article at Irrational Exuberance about anti-objects and reflective design. The author states, “The first – and only – programming paradigm I was taught at college was OO.” Another by a college CS tutor laments that object oriented design is the first (and often, the only) abstraction method taught to students in computer science programs.

I tend to agree with this thinking. OO is not the singular technique for code abstraction. It is useful because it maps closely to how the brain conceptualizes entities. Spot is a dog. Spot barks. Spot wags. Spot contains a mutable state that describes him, and he responds to certain inputs. Spot is an instance of canine, which in turn is an instance mammal, ad infinitum.

OO is a conceptually precise way to program, but it comes with a complexity and redundancy. As my mantra states, expressiveness > efficient > elegant > idiomatic. Redundancy and complexity get in the way of expressiveness.

The article at Irrational Exuberance mentions antiobjects as a way of avoiding some of the overhead and complexity problems of OO without abandoning OO completely. The context used is that of programming a MUD (with which I am just as guilty of wasting my teen years as the author). The program defines Player and Item, with individual items subclassing Item. A player can use an item. The author shows how the methods for equipping an item can be stored in the player object or the item object, and that the latter (an example of using antiobjects) was more efficient.

In fact, the latter is more expressive as well. It is not the person that determines how the item is used, but the item itself that describes its use (in the context of the game). A player would not eat a sword. This adds a logical constraint that does not mimic the real world – a player could eat a sword. It’s got iron in it. Very important for you, iron. Practically a health food.

But in the context of the program, that would be ridiculous and would require a heck of a lot of extra coding work, adding an enormous amount of complexity to the program. That’s a lot of work just to get a little extra iron in your diet.

One option the author did not explore at all is that OO might be overkill for the job. In a MUD, players and items are not overly complex concepts. A player had experience, level, items… and not much else (in many of them, at any rate). For something this simple, here is an approach that is at least as expressive, simpler, and easier to maintain. Although the author is using Ruby, this example is written in newLisp (using contexts as dictionaries), because it is seldom wise to miss an opportunity to write something in newLisp ;)

;;; 'item is a hash of tables
(context 'items "sword" '((use "wielded")))
(context 'items "chest-armor" '((use "torso")))
(context 'items "leg-armor" '((use "lets")))
 
;;; 'Player is a hash prototype. Defined using
;;; this syntax for clarity and simplicity.
(context 'Player)
(set 'inventory '())
(set 'wielded nil)
(set 'torso nil)
(set 'legs nil)
(context MAIN)
 
;;; Player actions
 
(define (own player item)
  "Adds item to player's inventory."
  (push item player:inventory))
 
(define (equip player item)
  "Equips an item from a player's inventory."
  (if (find item (context player "inventory"))
    (and
      (context player (lookup 'use
        (context items (string item))) item)
      (println "You equipped your " item))
    (println "You have no " item)))
 
;;; Now let's test it
 
(new 'Player 'Joe)
(own Joe 'sword)
(equip Joe 'sword)
(equip Joe 'leg-armor)

Including whitespace, comments, and examples at the end, the example is 35 lines. It also includes logic to verify that the player has an item in inventory and messages to the player in response to commands. Not bad. Expressive, efficient, and concise. And, because this version does not require the replication of many very similar objects, we are not wasting memory and avoiding a lot of introspecting code to keeps everything aligned.

Albert Einstein said, “Everything should be made as simple as possible, but not simpler.” OO is predicated on the concept of a program as a microcosm of the universe. This puts a lot of responsibility on a programmer’s shoulders. As a programmer, there is a tendency to want to create objects as extensible as possible; it becomes necessary to constrain each object to avoid unnecessary complexity. In many instances, that sort of extensibility is like sand-blasting a soup cracker. There are simpler abstraction techniques that reduce code complexity and redundancy and incidentally result in faster software.

Leave a comment | Trackback
Jun 8th, 2007 | Posted in Programming
Tags: ,
  1. Jan 27th, 2009 at 08:19 | #1

    Great article, giving some really relevant information.