Check out the new USENIX Web site.

8. What a Picture Is (Slight Return)

The process whereby we arrived at the definition of Picture was not as smooth as we described it in section 3. Let us continue the analysis we began there, and now consider the PIC (not FPIC) picture

box at last box.ne

This places a new box at the northeast corner of the most recently drawn box. The phrase "last box" suggests that a picture may depend upon the entire set of previously-drawn pictures. Assuming these are all collected into an environment, our definition of a picture would become

Picture = Environment -> ((GraphicsContext -> BitMap) x Environment)

(Our current definition does not allow the definition of last box, precisely because pictures do not see an incoming environment. The result is that we must assign a picture to an ML variable before we can access one of its named points.)

This is the "obvious" definition of Picture, and it was the first one we used. We worked with it for quite a while before deciding it was untenable. We still believe it is the correct definition, in principle, but it makes a clean integration into Standard ML impossible.

There are two problems with defining pictures in this way. The first is that it requires the redefinition of much of Standard ML. Consider a picture of the form (here we revert to FPIC-style syntax, though lastbox is not an FPIC primitive)

dbox at (lastbox pt "ne") 

dbox is a picture, so it has the type given above. lastbox is a function from environments to pictures. Thus, pt must have type

(Environment -> Picture) Name -> Point

so that the expression lastbox pt "ne" has type Environment -> Point. Thus, at has type

Picture x (Environment -> Point) -> Picture

So far, so good. But now consider

dbox at (1.0,2.0)

According to the type of at, which we just agreed upon, the expression (1.0,2.0) must be of type Environment -> Point, not Point! We might define a function

fun constantPoint p = (fn env => p);

and then we could write the expression above as

dbox at (constantPoint (1.0,2.0))

This is annoying enough, but now consider

dbox scale ((width lastbox) + 0.5) 

width lastbox is a function from environments to real numbers, but then what is the type of "+"? It cannot have type real x real -> real, so it is not the built-in multiplication of ML. Instead, it is a new multiplication operator of type

(Environment -> real) x real -> (Environment -> real).

Clearly, we are on a slippery slope: all the constants and built-in operators need to be "lifted" to the type Environment -> whatever.

To see the other problem with this definition of Picture, consider this example:

let val b = dbox
    val c = dcircle at (b pt "ne")
in b seq dtriangle seq c
end

The obvious intention is that the circle should be drawn at the northeast corner of the box. However, this is not what will happen. The way we have defined Picture, a picture is drawn only after all the previous pictures have been drawn. Thus, c is not drawn after b, but instead after the dtriangle. At that time, it will look in the environment, then calculate where the northeast point of a box would be if drawn at that time, and then draw the circle there. In short, there is no actual connection between box b and circle c.

Instead, something more like this would be needed:

let val b = namePic dbox "b"
    val c = dcircle at (lastpic "b" pt "ne")
in b seq dtriangle seq c
end

At the time c is drawn, lastpic finds the most recent picture named b and draws the circle there. Even this is not a direct connection between b and c; if dtriangle were instead a picture containing a picture named b, the circle would be drawn there.

In any case, we finally abandoned this approach as being too confusing.

So, our language design approach does not always work as well as we would hope. We would like to make two observations, however, before ending this discussion. One is that the two problems we've described are problems with integrating the new primitives into the existing language. In particular, the problems arise from the inescapable distinction between ML's ordinary variable environment and the picture environments created by FPIC primitives. Neither problem would exist, as far as we can see, if FPIC were designed as a new language. (In fact, the first problem is already partially solved in the functional language Haskell [3], in that literals and built-in operations can be "lifted" in the way that we require).

Our second observation is that the technical problem described in this section should not be considered to imply that the language design is a failure. We still consider that our original thesis has been substantially borne out.


[ Prev | Top | Next ]