Instead, I thought I'd have to go with a model analogous to Schrodinger's cat, where a composed object's methods can be bound to multiple implementations at the same time. Each of those implementations is run, and the results are themselves composed into a single object for the result value. Like a cat that's both alive and dead, the method is bound to both implementation A and implementation B — and its result is both A' and B'.
There's a certain beauty to that, but it's pretty complicated. I also suspected it may result in slow code as objects get more and more complicated at runtime. So I thought some more and came up with a solution that leads to a simple, non-Schrodinger object.
But maybe these concerns are premature on my part. I'm basically trying to do two optimizations — for simplicity, and for performance — without having measured how bad they are.
On the other other hand, back when I first proposed the Schrodinger approach, I noted that eventually you'll need to collapse the wave function, so to speak. At the end of the day, you need to print "I'm a tea pot" or
3.14159; you can't print their superpositioned composition.
So then, maybe the solution is to use both approaches. The Schrodinger approach will be the standard composition mechanism (not in any formal way, but as a coding recommendation), while the "simple" approach will be used to collapse the wave.
To anchor things, let's call the standard composition operator
<>, and the simple one
</(you'll see why in a bit).
I'll also define a reserved type
Simplewhich can only be composed into other objects using the simple composition operator. So, if a method takes
Simple Foo Barand you have a
Foo Bar(which may be superpositioned), you need to compose it using the simple operator:
Simple </ myFooBar.
This handles the print case, for instance.
stdout.printwill take a
Simple String, and it's up to the caller to ensure that its variable is already
Simple, or to collapse it otherwise.
One of the lingering questions I had in the back of my mind was what to do when two objects of the same type are composed:
SomeInt(1) <> SomeInt(2). Now the answer seems obvious. With the Schrodinger composition operator, just superposition them both in. With the simple operator, the left one will get folded in first, and the right one will then collide and be discarded.
That's why I picked
</as the operator. It looks like it points to the left, and thus conveys that the left operand has precedence in case of collisions.
There's not really a compelling argument for this complexity. Why don't I just stick with the simple composition approach? Basically because I think the Schrodinger variant seems fun and worth playing around with.