I think the 'scope' keyword may resolve issue. Attributes of lazy parameter in template function belong to caller side.
int foo()(lazy int value) @safe pure /*nothrow*/ { return value(); } void main() { int n = foo(10); // evaluating lazy parameter never break safety and purity of foo. // (violating nowthrow-ness might be a bug.) // because the violation belongs to caller side - it is main function. } Similarly, scope delegate body always in caller side, so calling it means temporary exiting to caller side. void foo(ref inout int x, scope void delegate(ref inout(int)) dg) { dg(x); } void main() { int a; foo(a, (ref int x){ x = 10; }); // don't break foo's inout-ness, so should be allowed assert(a == 10); } How about? Kenji Hara 2012/2/19 Stewart Gordon <smjg_1...@yahoo.com>: > At the moment, if a function has an inout parameter, it must have an inout > return type. > > But this prevents doing stuff like > > void test(ref inout(int)[] x, inout(int)[] y) { > x = y; > } > > or passing the constancy through to a delegate instead of a return value. > > A typical use case of the latter is to define an opApply that works > regardless of the constancy of this and allows the delegate to modify the > iterated-through objects _if_ this is mutable. > > int opApply(int delegate(ref inout(T)) dg) inout; > > But then I realised a potential ambiguity: > (a) the constancy is passed through to the delegate > (b) the delegate has an inout parameter in its own right > > If we go by interpretation (b), then each signature contains only one inout, > so even if we relaxed the rules to allow this it would just be equivalent to > > int opApply(int delegate(ref const(T)) dg) const; > > however, this won't always be true in the general case. > > The essence of functions with inout parameters is that they have a hidden > constancy parameter. This is essentially a template parameter, except that > only one instance of the function is generated, rather like Java generics. > If we made this parameter explicit in the code, we could distinguish the > two meanings: > > (a) int opApply(constancy K)(int delegate(ref K(T)) dg) K; > (b) int opApply(constancy K)(int delegate(constancy L)(ref L(T)) dg) K; > > Moreover, in case (a), opApply would accept for dg: > - an int delegate(ref T) only if this is mutable > - an int delegate(ref immutable(T)) only if this is immutable > - an int delegate(ref const(T)), or a delegate that is itself > constancy-templated, regardless of the constancy of this > > Perhaps the simplest example where meaning (b) is actually useful is > > inout(char)[] process(inout(char)[] delegate(inout(char)[]) dg, > inout(char[]) text) { > return text; > } > > but still, somebody might want meaning (a). Anyway, under DMD 2.058 (Win32) > this gives > > inout_delegate.d(1): Error: inout must be all or none on top level for > inout(char)[](inout(char)[] function(inout(char)[]) fn, inout(char[]) text) > > but why? At least it seems that DMD acknowledges the ambiguity, even if the > error message doesn't make sense. > > > The question really is: When inout is applied both to a parameter in a > function's signature and to something in the signature of a > function/delegate parameter therewithin, how should it be interpreted? The > spec doesn't seem to address the issue at all. Indeed, we can ask two > things: > - what actually does the compiler make of it at the moment? > - what would be the ideal way for it to work? > > Possibilities I can see: > > - always (a) > - always (b) > - (a) if at either level inout only occurs once, otherwise (b) (probably > undesirable because of fragility) > - just reject such signatures as ambiguous > > And moreover, should we support some syntax (similar to what I've used here > or otherwise) to state explicitly whether we want to pass the constancy > through to the delegate signature or not? > > Stewart.