archives

Why Object-Oriented Languages Need Tail Calls

The Fortress blog has a recent post, Why Object-Oriented Languages Need Tail Calls, where Guy Steele argues for the necessity of proper tail call implementations without rehashing two of the classic arguments: state machines and the continuation passing style. It starts by mentioning William Cook's On Understanding Data Abstraction, Revisited:

In this blog post we extend one of his examples in order to make a completely different point: object-oriented programming languages need tail calls correctly implemented, not just as a "trivial and optional" optimization of the use of stack space that could be achieved by using iteration statements, but in order to preserve object-oriented abstractions.

The post also mentions other papers previously discussed on LtU: Automata as Macros, and A Tail-Recursive Machine with Stack Inspection.

Pragmatic declarative event abstraction

My recent exploration of Rx has gotten me thinking again about events. Ever since I read Conal's original Fran paper, I've been fascinated and confused by the relationship between discrete streams of events vs. continuous behaviors. Although they can be wrapped up in one abstraction (signals), this unification leads to pragmatic if not semantic mismatches resulting from the difference between discrete and continuous.

Bling, my current language effort, provides very concise syntax for creating continuous relationships between properties; e.g., Matrix.Bind = Matrix4Bl.MakeScale(.8d) * Rotate would bind a matrix property to a rotation matrix scaled down 80%. The programmer doesn't need to create a value converter, populate a binding object, or even express a closure. On the other hand, the programmer has to manipulate values indirectly because they are "lifted." This is problematic when we need to change a property's value discretely. Consider updating a 3D quaternion rotation via a virtual trackball. The idea is to take a 2D mouse position and convert it into a 3D sphere position:

var Mouse2D = sphere.Mouse.Position / sphere.Size;
Mouse2D = (Mouse2D * 2d - 1d);
var Mouse3D = MouseAt.AddZ((1d - Mouse2D.LengthSquared).Max(0d).Sqrt).Normalize;

Now, we want to update the Rotate property, and for efficiency reasons, we want to generate code that does this update:

var Rotation0 = this.Temporary(QuaternionBl.Identity);
var Mouse3D0 = this.Temporary(Double3Bl.Zero);
Action Begin0 = Rotation0.MkAssign(Rotation);
Action Begin1 = Mouse3D0.MkAssign(Mouse3D);
Action Update = Rotation.MkAssign(Mouse3D.AngleBetween(Mouse3D0) * Rotation0);

Rotation0 and Mouse3D0 are explicit temporary properties that remember the 3D mouse position and initial rotation when a rotation begins. The update then takes the current 3D mouse position, finds its angle with the remembered 3D mouse position, and prepends that to the remembered quaternion rotation. Finally, these actions are used in a drag state machine subscription:

DragBegin.Subscribe(() => { Begin0(); Begin1(); });
DragUpdate.Subscribe(() => Update());

This was the typical way to handle events in Bling, where a lot behavior occurs by updating and manipulating UI properties. This solution was sub-optimal for the following reasons:

  • Update actions and subscriptions are separate steps.
  • Explicit temporaries are ugly, and we still have to create update actions and subscriptions for these temporaries.

Then I realized we could treat events themselves as indices of "time" into a lifted values, much like an integer is an index of space into an array. So a[e] represents the value of expression a when event e fires, in essence e is like a continuation. This is how things work in Rx; e.g.,

from mouseLeftButtonDown in this.GetMouseLeftButtonDown()
let rotation0 = this.Rotate.CurrentValue
let position0 = Mouse.GetPosition(this)
...

where "let" binds in the context of mouseLeftButtonDown event/continuation. So I thought, that's essentially what I'm writing so much code to do anyways, so let's do that in Bling. Also, we can flip the syntax to also control assignment: a[e] = ... would cause a value to be pushed into the a expression when e fires. Redoing my virtual trackball example in the new syntax:

Rotate[outer.Mouse.Drag.Update] =
  Mouse3D.AngleBetween(Mouse3D[outer.Mouse.Drag.Enter]) * Rotate[outer.Mouse.Drag.Enter];

No temporaries, updates, or subscriptions, just an assignment: so much better! An artifact of my approach is that events can't directly carry data payloads; they are just points in time. However, I've solved this problem (for now) by making event data available via dynamic context.

Done before? Crazy language idea?