On Thu, 29 Sep 2011 16:32:45 -0400, dsimcha <[email protected]> wrote:
Immutability and type propagation are two of D's greatest assets. Below
is an
example of where they would be difficult to take advantage of:
int foo;
if( bar < 3) {
try {
foo = doStuff(someOtherStackVariables);
} catch(SomeException) {
foo = 666;
}
} else {
foo = 4;
}
I've recently discovered this pattern to allow use of type propagation
and
immutability in these situations with no extra boilerplate:
immutable foo = {
if( bar < 3) {
try {
return doStuff(someOtherStackVariables);
} catch(SomeException) {
return 666;
}
} else {
return 4;
}
}();
This allows me to make foo immutable and have its type inferred while
avoiding
any boilerplate code, but has four problems:
1. It allocates a closure. Therefore, it can't be used in
performance-critical code. We really need to optimize away closure
allocation
in trivial cases where the function pointer obviously can't escape, to
make
delegate literals more useful in performance-critical code.
2. Delegate literals can't be inlined in even the most trivial cases.
This
is less severe than (1), and probably would almost never have a
non-negligible
impact in practice, though.
3. A function that uses a delegate literal can't be pure because purity
is
not inferred for delegate literals:
void main() {
foo();
}
void foo() pure {
int z;
int i = {return z + 1;}();
}
Error: pure function 'foo' cannot call impure delegate '__dgliteral1'
4. Auto return type inference is limited to cases where all return
statements
return the exact type. Is there any reason I'm missing why, if multiple
return statements with different types are present, an auto return
function
couldn't just return a common type?
+1 on all 4 issues.
Note that there is a workaround for 1:
DT noclosure(DT)(scope DT d) if(is(DT == delegate))
{
return d;
}
Also note that next to memory allocation, lack of inlining is the biggest
performance problem. It's not negligible (in case that is what you were
saying, hard to tell from all the negatives ;)
-Steve