rvalue types

Simen Kjærås simen.kjaras at gmail.com
Mon Mar 12 13:59:59 UTC 2018


There's been a discussion[0] over in D.learn about a library-only 
implementation of properties. One of the ideas mentioned there is 
rvalue types - types that automatically decay into a different 
type when passed to a function or assigned to a variable. They 
are useful for property wrappers like this, and when you want to 
perform a series of operations on something before performing 
some final step.

An example of the latter is `a ^^ b % c` for BigInts, where the 
naïve way would be horribly inefficient, and there's a much 
better way of doing it, which requires more knowledge of the 
operations involved. If `a ^^ b` returned an rvalue type that 
either decays to a regular BigInt or acts as the LHS in `tmp % 
c`, it would have the necessary information and be able to do the 
right thing.

Another use case is a chain of operations, e.g. fluent 
initialization:

Widget.create()
     .width(35)
     .height(960)
     .data(readData())
     .Done();

Where in current D the Done() step needs to be explicit, an 
rvalue type would automatically call Done when the result is 
assigned to a variable or passed to a function.

The problem with such a set of types, of course, is that 
`typeof(functionThatReturnsRvalueType())` will be different from 
`typeof((){ auto t = functionThatReturnsRvalueType(); return 
t;}())`, and that bleeds into documentation. It may also be 
confusing that `return a ^^ b % c;` is much faster than `auto tmp 
= a ^^ b; return tmp % c;`.

An example of how they would work:

struct ModularExponentiationTemporary {
     BigInt lhs, rhs;

     @rvalue // Or however one would mark it as such.
     alias get this;

     BigInt get() {
         return pow(lhs, rhs);
     }

     BigInt opBinaryRight(string op : "%")(BigInt mod) {
         return modularPow(lhs, rhs, mod);
     }
}

unittest {
     BigInt b = 4;
     BigInt e = 13
     BigInt m = 497;

     // b ^^ e returns a ModularExponentiationTemporary,
     // and its opBinaryRight() is immediately invoked.
     auto fast = b ^^ e % m;

     assert(is(typeof(fast) == BigInt));
     assert(fast== 445);

     // b ^^ e returns a ModularExponentiationTemporary,
     // and its get() method is immediately invoked.
     auto slowTmp = b ^^ e;
     auto slow = slowTmp % m;

     assert(is(typeof(slowTmp == t) == BigInt));
     assert(is(typeof(slow) == BigInt));
     assert(slow == 445);
}

Is this an interesting concept? Are there other use cases I 
haven't covered? Can this be done with existing language 
features? Are there problems I haven't foreseen?

--
   Simen

[0]: 
https://forum.dlang.org/post/mqveusvzkmkshrzwsgjy@forum.dlang.org


More information about the Digitalmars-d mailing list