## digitalmars.D - proxies

- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Jan 25 2009
- Jason House <jason.james.house gmail.com> Jan 25 2009

Proxying is a useful technique for achieving nice effects like e.g. expression templates. Consider, for example, a matrix operators package that returns "lazy" matrices in the form of expression templates: struct Matrix { LazyMatrix!("a + b") opAdd(ref Matrix another) { ... } ... } Now if Matrix also defines construction and assignment from LazyMatrix, it's all fine and dandy because we can write: Matrix a, b; Matrix c = a + b; // no temporary matrix created! a = b + c; // no temporary matrix created! The problem is that auto works against all this: Matrix a, b; auto c = a + b; // c is _not_ a Matrix! Using c directly has any number of unpleasant surprises in store for the unwitting user. For example, modifying a and b also modifies c. And since c does the addition on the fly, using c[i, j] directly will actually be slower than using a matrix! How could this effect be avoided? In the past I suggested that proxy objects should be private and offer opImplicitCast to the desired types. Then the compiler figures that out and generates code appropriately. Another solution would be to define opAuto that dictates what happens when an automatic object is instantiated from the proxy type: struct LazyMatrix(alias expression) { Matrix opAuto() { ... } } Then writing: auto c = a + b; will automatically make c a Matrix and initialize it as prescribed by opAuto. On the other hand, if the type is explicitly requested, the proxy stays in vigor: LazyMatrix!("a + b") cproxy = a + b; This is, of course, useful when code is interested in capturing the proxy itself. One problem I see with this is template instantiation: void f(T)() { ... } f(a + b); What should T be? LazyMatrix or Matrix? Another problem is, what does typeof do? typeof(a + b) c = a + b; Should that be same as auto, or should typeof tell the truth? Any way you choose, some of the current assumptions will break. So I'm hoping someone here will come with a better idea. Andrei

Jan 25 2009

Andrei Alexandrescu wrote:Proxying is a useful technique for achieving nice effects like e.g. expression templates. Consider, for example, a matrix operators package that returns "lazy" matrices in the form of expression templates: struct Matrix { LazyMatrix!("a + b") opAdd(ref Matrix another) { ... } ... } Now if Matrix also defines construction and assignment from LazyMatrix, it's all fine and dandy because we can write: Matrix a, b; Matrix c = a + b; // no temporary matrix created! a = b + c; // no temporary matrix created! The problem is that auto works against all this: Matrix a, b; auto c = a + b; // c is _not_ a Matrix! Using c directly has any number of unpleasant surprises in store for the unwitting user. For example, modifying a and b also modifies c. And since c does the addition on the fly, using c[i, j] directly will actually be slower than using a matrix! How could this effect be avoided? In the past I suggested that proxy objects should be private and offer opImplicitCast to the desired types. Then the compiler figures that out and generates code appropriately. Another solution would be to define opAuto that dictates what happens when an automatic object is instantiated from the proxy type: struct LazyMatrix(alias expression) { Matrix opAuto() { ... } } Then writing: auto c = a + b; will automatically make c a Matrix and initialize it as prescribed by opAuto. On the other hand, if the type is explicitly requested, the proxy stays in vigor: LazyMatrix!("a + b") cproxy = a + b; This is, of course, useful when code is interested in capturing the proxy itself. One problem I see with this is template instantiation: void f(T)() { ... } f(a + b); What should T be? LazyMatrix or Matrix? Another problem is, what does typeof do? typeof(a + b) c = a + b; Should that be same as auto, or should typeof tell the truth? Any way you choose, some of the current assumptions will break. So I'm hoping someone here will come with a better idea. Andrei

What if a+b has a polysemous return type of Matrix or LazyMatrix, with a default of Matrix? That should operate like string literals. If Matrix was implicitly castable to LazyMatrix (but not vice versa), then operations could be written based on LazyMatrix. The user could pass in Matrix and it'd work, and function calls with a polysemous value would use LazyMatrix instead of Matrix to avoid the implicit cast. Of course, that solution sounds like it requires expanded compiler support for polysemous values and requires LazyMatrix to not be implicitly castable to Matrix. I also don't know if implictly casting Matrix to LazyMatrix makes sense.

Jan 25 2009