inout and function/delegate parameters

Timon Gehr timon.gehr at gmx.ch
Sun Feb 19 07:31:39 PST 2012


On 02/19/2012 03:27 PM, Stewart Gordon wrote:
> 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.

I think some kind of disambiguation is essential.

I see multiple solutions:

1.

(b) would be the default. We could use the some parameter storage class 
to opt-in a):

(a) int opApply(@somequalifier int delegate(ref inout(T)) dg) inout;
(b) int opApply(int delegate(ref inout(T)) dg) inout;

This would work well. Maybe this is a little bit hackish, but I propose 
to use @somequalifier:=inout

a) int opApply(inout int delegate(ref inout(T)) dg) inout;
b) int opApply(constancy K)(int delegate(constancy L)(ref L(T)) dg) K;

Actually affecting the constancy of the delegate itself is not an useful 
operation anyway, but it could still be achieved:

a) int opApply(inout inout(int delegate(ref inout(T)))) dg) inout; // 
every inout means the same thing
b) int opApply(inout(int delegate(ref inout(T)))) dg) inout; // the 
inout on the delegate parameter is distinct from the other two

2.

We introduce an infinite number of wildcard storage classes:
inout, inout', inout'', inout''', inout'''', ...

a) int opApply(int delegate(ref inout(T)) dg) inout;
b) int opApply(int delegate(ref inout'(T)) dg) inout;

This would have the additional boon that it would allow to type check a 
larger set of const-correct functions [1], like the following:

void swapPairs(ref inout(int)[] x1, ref inout'(int)[] y1,
                ref inout(int)[] x2, ref inout'(int)[] y2){
     swap(x1,x2);
     swap(y1,y2);
}


[1] furthermore, we'd finally get identifier' identifiers ;)







More information about the Digitalmars-d mailing list