Composability and orthogonality in language design

Each time I delve into the HTML/CSS world, I am reminded of why I love the Algol-derived languages.

Algol was the ancestor of modern imperative languages like C# and Java. It introduced the concepts of orthogonality and composability to computer language design. Orthogonality is the property of a construct that means it does not intersect with others. There is just one way to express any single idea. Composability is the property of constructs that they can be combined to express larger ideas.

Java is an excellent example of an orthogonal, composable language. Classes, methods, statements, and expressions are all different things (orthogonal). If you want to describe a type of object, you use a class. If you want to describe behavior, it's a method. Through composition, you can describe more complex ideas, like the behaviors of types (class + methods), anonymous types (class + expression), and loops (statement + expressions).

C# relaxes the ideas just a little. For example, a delegate is both a type (class) and a behavior (method). Properties look like members, but act like methods. Since these language constructs serve dual purposes, there is sometimes more than one way to express a simple idea. This was done in the name of convenience, at the expense of language purity.

HTML and CSS
Contrast this with the two ubiquitous languages of the web.

HTML is composable, since you nest elements to construct pages. But these constructs are not orthogonal. An image, for example, could be represented either by an <img> tag or by a <div> with a background-image style. HTML lack of orthogonality is due mainly in part to legacy, as CSS obsoletes elements like <b>.

CSS lacks both composability and orthogonality. Cascading is a limited set of rules, not a true composition mechanism. And several of the attributes interact such that you cannot control one without influencing another.

The "Ken Burns" effect
For example, I am currently implementing the "Ken Burns" effect in Javascript. Images zoom, pan, and fade into one another. Yes, this would be better done in Silverlight or Flash (both highly composable and orthogonal, by the way). But I'd like a solution that requires no plug-in. And having had success with cover flow, I thought I would give it a try.

For the effect, I need to fade from one image to another. So the two images must overlap. In addition, I need to pan and zoom within the frame. This requires that I crop the images. CSS has constructs for both. Unfortunately, they interact.

There are two interrelated concepts at play in the placement of elements on a CSS-based web page: location and layout. Location is the placement of an element itself, whereas layout determines how an element affects the placement of other elements around it.

Position and overflow
The position attribute in CSS affects both placement and layout. The default position is "static", which means that it is entirely under the control of layout. To move an element, you usually set the position to "absolute". This not only gives you control of its position, but also takes it out of the layout equation. For panning, you set position to "absolute".

Now to get the clipping working. We can put the images inside a <div> to define the clip region. Setting overflow of the <div> to "hidden" causes all child objects that fall outside its borders to be cropped.

Unfortunately, for reasons I have yet to comprehend, "overflow: hidden" does not affect child elements with "position: absolute". One attribute controls clipping, while the other controls placement. They should be orthogonal, but they are not.

To solve this problem, I have to select a strange value for position. The position "relative" is properly clipped. However, it combines the ideas of location and layout. Relative positioning is not relative to the containing <div>, as you might expect. Instead, it is relative to the object's resting place as determined by layout. To further confuse the issue, layout proceeds around the object as if it was not moved.

Since I have two images inside the <div> - one fading into the other - the layout of the second is going to depend upon the size of the first. I have to do some extra math based on the size of the first image to adjust the placement of the second. Furthermore, when I switch to a new image, I need to quickly readjust that compensation. I haven't yet determined how to accomplish this without causing some kind of flicker.

Because the CSS attributes are non-orthogonal, I have to solve a multi-variable problem to determine which CSS properties to use to express any given idea. Maybe a plug-in is not so bad.

Leave a Reply

You must be logged in to post a comment.