Slippery Types

January 16, 2009

Sometimes, you want to create new types with a constructor-like interface. Sometimes, inheritance doesn’t capture what you really want to do. In these cases, typical object-oriented programming languages get in your way.

A first example.

Let’s say you want to model an HTTP response object. Forgive my rusty Java. I would argue that the following represents the most natural way of expressing the object.

class HTTPResponse {
    int responseCode;
    Map<String, String> headers;
    String entityBody;
    ...
}

 

 

Now, you realize that an HTTP 201 (“Created”) response always has a Location header. You want to provide some programmatic support for this. There are many ways to model this, but let’s walk through the most intuitive thought process.

A Created response is a HTTP Response. This encourages us to use inheritance.

class HTTPCreatedResponse extends HTTPResponse {
...
}

 

 

All HTTPCreatedResponses have a responseCode of 201. This is a property of the HTTPCreatedResponse *class*.

class HTTPCreatedResponse extends HTTPResponse {
	protected static final int responseCode = 201;
}

 

 

But, but, but. responseCode is an instance variable of HTTPResponse. We don’t want to specify it in two places. Our intuitions have led us into a corner.

Note that I am not saying “YOU CANNOT MODEL THIS IN JAVA.” Instead I make the gentler claim that one cannot model this pattern intuitively.

The problem of slippery subtypes is common. Sometimes it manifests itself as a desire for polymorphic class variables:

class HTTPResponse {
	static int responseCode;
	int getResponseCode() { return responseCode; }
	int setResponseCode(responseCode) { this.responseCode = responseCode; }
	...
	public HTTPResponse(responseCode, ...) { ... }
}

class HTTPOKResponse extends HTTPResponse {
	static final int responseCode = 200;
	...
}

class HTTPCreatedResponse extendsHTTPResponse {
	static final int responseCode = 201;
	String location;
	public HTTPCreatedResponse(location, ...) { this.location = location; ... }
}

...

HTTPOKResponse(...).getResponseCode() /* 200 */
HTTPCreatedResponse(...).getResponseCode() /* 201 */

 

 

Consider modeling symbols in a parser. It is common to see code like:


STATEMENT_TERMINATOR = new Symbol(";");
BLOCK_OPEN = new Symbol("{");
BLOCK_CLOSE = new Symbol("}");

Having done this, we might wish to later instantiate these symbols when we encounter them, as if new STATEMENT_TERMINATOR(line, column) were possible. But it is not. It’s not quite inheritance. We don’t want actually care too much about subtype substitutability. A functional programmer might think of this as a desire to curry types, in a way. A programmer in a prototype-inheritance language would just chuckle condescendingly and be on her way.

Advertisements

2 Responses to “Slippery Types”

  1. jon Says:

    Hrm.. I know the frustration here. But maybe it’s intuition that’s slippery though. Not being in the javascript headspace I’d probably have gone right along without trouble and initialised responseCode in a subclass constructor, and made an instance of a symbol factory.

    Aren’t there things a Java-head would find counter-intuitive in Javascript?


Comments are closed.

%d bloggers like this: