Jump to page: 1 2 3
Thread overview
inout and function/delegate parameters
Feb 19, 2012
Stewart Gordon
Feb 19, 2012
Timon Gehr
Feb 19, 2012
Stewart Gordon
Feb 19, 2012
Timon Gehr
Feb 19, 2012
kenji hara
Feb 19, 2012
Timon Gehr
Feb 25, 2012
Timon Gehr
Mar 05, 2012
Stewart Gordon
Mar 05, 2012
Timon Gehr
Mar 06, 2012
Timon Gehr
Mar 07, 2012
Stewart Gordon
Mar 07, 2012
Stewart Gordon
Mar 08, 2012
Timon Gehr
Mar 08, 2012
Timon Gehr
Mar 08, 2012
Stewart Gordon
Mar 08, 2012
Timon Gehr
Mar 06, 2012
Stewart Gordon
Mar 10, 2012
Stewart Gordon
February 19, 2012
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.
February 19, 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 ;)





February 19, 2012
On 19/02/2012 15:31, Timon Gehr wrote:
<snip excessive quote>
<snip>
> 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

So "inout inout" would be a special token sequence to link the outer and inner inouts together.

> b) int opApply(inout(int delegate(ref inout(T)))) dg) inout; // the inout on the delegate
> parameter is distinct from the other two

I don't see what the inout applied to the type of dg is actually doing here....

> 2.
>
> We introduce an infinite number of wildcard storage classes:
> inout, inout', inout'', inout''', inout'''', ...
<snip>
> [1] furthermore, we'd finally get identifier' identifiers ;)

The lexer would need to be tweaked to support these.  And it might get confusing to overload ' like this, given that some other keywords can be immediately followed by a character literal.

Maybe inout$ with some kind of identifying token....

(a) int opApply(int delegate(ref inout$0(T)) dg) inout$0;
(b) int opApply(int delegate(ref inout$1(T)) dg) inout$0;

Hmm....

Stewart.
February 19, 2012
On 02/19/2012 06:09 PM, Stewart Gordon wrote:
> On 19/02/2012 15:31, Timon Gehr wrote:
> <snip excessive quote>
> <snip>
>> 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;
>
> ???

oops...

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

>
>> 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
>
> So "inout inout" would be a special token sequence to link the outer and
> inner inouts together.

No, but probably it is more clear with the fixed b) case above. The inout parameter storage class would be used for linking.

>
>> b) int opApply(inout(int delegate(ref inout(T)))) dg) inout; // the
>> inout on the delegate
>> parameter is distinct from the other two
>
> I don't see what the inout applied to the type of dg is actually doing
> here....
>
>> 2.
>>
>> We introduce an infinite number of wildcard storage classes:
>> inout, inout', inout'', inout''', inout'''', ...
> <snip>
>> [1] furthermore, we'd finally get identifier' identifiers ;)
>
> The lexer would need to be tweaked to support these. And it might get
> confusing to overload ' like this, given that some other keywords can be
> immediately followed by a character literal.

I am not aware of any except 'case'. But you are right. It would certainly confuse most existing D editors.

>
> Maybe inout$ with some kind of identifying token....
>
> (a) int opApply(int delegate(ref inout$0(T)) dg) inout$0;
> (b) int opApply(int delegate(ref inout$1(T)) dg) inout$0;
>
> Hmm....
>
> Stewart.

That would be a possibility.
February 19, 2012
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_1998@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.
February 19, 2012
On 02/19/2012 11:27 PM, kenji hara wrote:
> 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
>

If I get you right, then this cannot work in general.

immutable int y=0;
void foo(ref inout int x, scope void delegate(ref inout(int)) dg) {
    dg(y); // boom
}
February 24, 2012
On Sun, 19 Feb 2012 09:27:42 -0500, Stewart Gordon <smjg_1998@yahoo.com> 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;
>      }

This is a legitimate request, I think there is an effort underway by myself Timon and Kenji to make this work.  (well mostly Kenji and Timon)

> 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;

What you ask isn't possible given the current design of inout.  During inout function execution, inout is a special form of const, even if the object on which opApply is being called is mutable.

> 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;

Yes, this is what I think it should be equivalent to.  As I said, inside opApply, inout is like const, and is transitive.  So you cannot "temporarily" make it mutable.

> 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:

No the constancy is not a parameter  (not even a hidden one).  The magic of inout happens at the call, not inside the function.  Inside, it's just another type of const.

However, there is an entire part of delegates that is yet untapped -- implicit conversion of delegates.  For example, int delegate(ref const int x) could be implicitly converted to int delegate(ref int x).  Maybe there is something there that can be used to solve this problem.  Maybe there is a rule we could apply when calling an inout-enabled function that allows implicit conversion of a delegate to an inout -flavored version of the delegate (as long as the normal delegate matches the decided inout constancy factor).  But that violates transitivity.  I'm not sure Walter would go for this.

-Steve
February 25, 2012
On 02/24/2012 05:26 PM, Steven Schveighoffer wrote:
> On Sun, 19 Feb 2012 09:27:42 -0500, Stewart Gordon <smjg_1998@yahoo.com>
> 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;
>> }
>
> This is a legitimate request, I think there is an effort underway by
> myself Timon and Kenji to make this work. (well mostly Kenji and Timon)
>
>> 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;
>
> What you ask isn't possible given the current design of inout. During
> inout function execution, inout is a special form of const, even if the
> object on which opApply is being called is mutable.
>

The call site has enough information to type check the call given that there is some syntax to tie the two inout qualifiers together. inout shouldn't imply const, we already have const for that.

>> 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;
>
> Yes, this is what I think it should be equivalent to. As I said, inside
> opApply, inout is like const, and is transitive. So you cannot
> "temporarily" make it mutable.
>

inout means 'some qualifier but we don't know which one'. The call site on the other hand knows which qualifier it is. If the call is made to work there is no 'making it mutable', because it is mutable all the time. The inout function cannot change the data because it cannot know what the constancy is.


>> 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:
>
> No the constancy is not a parameter (not even a hidden one). The magic
> of inout happens at the call, not inside the function.

The same is (mostly) true for Java generics.

> Inside, it's just another type of const.
>
> However, there is an entire part of delegates that is yet untapped --
> implicit conversion of delegates. For example, int delegate(ref const
> int x) could be implicitly converted to int delegate(ref int x). Maybe
> there is something there that can be used to solve this problem. Maybe
> there is a rule we could apply when calling an inout-enabled function
> that allows implicit conversion of a delegate to an inout -flavored
> version of the delegate (as long as the normal delegate matches the
> decided inout constancy factor). But that violates transitivity. I'm not
> sure Walter would go for this.
>

Unfortunately this violates type safety. Also see:
http://d.puremagic.com/issues/show_bug.cgi?id=7542




March 05, 2012
On Sat, 25 Feb 2012 09:02:47 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 02/24/2012 05:26 PM, Steven Schveighoffer wrote:
>> On Sun, 19 Feb 2012 09:27:42 -0500, Stewart Gordon <smjg_1998@yahoo.com>
>> 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;
>>> }
>>
>> This is a legitimate request, I think there is an effort underway by
>> myself Timon and Kenji to make this work. (well mostly Kenji and Timon)
>>
>>> 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;
>>
>> What you ask isn't possible given the current design of inout. During
>> inout function execution, inout is a special form of const, even if the
>> object on which opApply is being called is mutable.
>>
>
> The call site has enough information to type check the call given that there is some syntax to tie the two inout qualifiers together. inout shouldn't imply const, we already have const for that.

inout is defined as "during this function execution the given parameter will be treated as const".  This is necessary to have a single implementation for all flavors of const.

Remember, inside the function the compiler has no idea that the actual data is mutable, const or immutable.  One of the cornerstones of inout is that it must generate one function.

>>> 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;
>>
>> Yes, this is what I think it should be equivalent to. As I said, inside
>> opApply, inout is like const, and is transitive. So you cannot
>> "temporarily" make it mutable.
>>
>
> inout means 'some qualifier but we don't know which one'. The call site on the other hand knows which qualifier it is. If the call is made to work there is no 'making it mutable', because it is mutable all the time. The inout function cannot change the data because it cannot know what the constancy is.

What you would propose is that a delegate which takes a const/mutable/immutable implicitly translates to one which takes an inout.  I agree it can be made to work, but it does not fit into the current definition of inout.

It would be ideal for inout to solve this, but it's not chartered in such a way to do so.  It's currently transitive, and this would break transitivity.  If we want to look at fundamentally redefining inout so that it can break transitivity, then we can look at that.  But I don't think this is a simple "add-on" to the current functionality.

>>> 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:
>>
>> No the constancy is not a parameter (not even a hidden one). The magic
>> of inout happens at the call, not inside the function.
>
> The same is (mostly) true for Java generics.
>
>> Inside, it's just another type of const.
>>
>> However, there is an entire part of delegates that is yet untapped --
>> implicit conversion of delegates. For example, int delegate(ref const
>> int x) could be implicitly converted to int delegate(ref int x). Maybe
>> there is something there that can be used to solve this problem. Maybe
>> there is a rule we could apply when calling an inout-enabled function
>> that allows implicit conversion of a delegate to an inout -flavored
>> version of the delegate (as long as the normal delegate matches the
>> decided inout constancy factor). But that violates transitivity. I'm not
>> sure Walter would go for this.
>>
>
> Unfortunately this violates type safety. Also see:
> http://d.puremagic.com/issues/show_bug.cgi?id=7542

That bug does not exactly capture what I said.  The inout translation *must* happen at the same point where inout is resolved at the call site.

For example, you can't do this:

int delegate(inout(int)*) iod;
int delegate(int *) md;
iod = md; // error, we don't know what inout will resolve to.

But this could be made to work:

void foo(inout(int)* x, int delegate(inout(int)*) d) { d(x);}

int x;
foo(&x, md); // translation is valid, because we know what inout resolves to.
foo(&x, iod); // also valid, because iod must have been defined to actually take an inout parameter.

Again, if you look at inout as a form of const, this breaks transitivity, it's kind of a cousin of logical const.  Walter needs to be involved to have his say.  I think it could be made to work.

The more I think about it, the more I think this is worth changing.  inout provides a unique way to make something that is normally a template that generates multiple copies of the same code into one that generates one copy.  The large benefit of it is you can write one function instead of 3, and still have it not be a template.

-Steve
March 05, 2012
On 05/03/2012 13:49, Steven Schveighoffer wrote:
> On Sat, 25 Feb 2012 09:02:47 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
<snip>
>> inout means 'some qualifier but we don't know which one'. The call site on the other
>> hand knows which qualifier it is. If the call is made to work there is no 'making it
>> mutable', because it is mutable all the time. The inout function cannot change the data
>> because it cannot know what the constancy is.
>
> What you would propose is that a delegate which takes a const/mutable/immutable implicitly
> translates to one which takes an inout. I agree it can be made to work, but it does not
> fit into the current definition of inout.

I'm not sure if you understand correctly.  Is isn't a matter of implicit conversion of delegates from one type to another, but a matter of (in this case) opApply accepting a delegate with a parameter whose constancy matches that of this.

You can think of a function signature with an inout parameter as being really a union of three function signatures.

    inout(T)[] func(inout(T)[] param)

is a union of

    T[] func(T[] param)
    immutable(T)[] func(immutable(T)[] param)
    const(T)[] func(const(T)[] param)

In the same way, the essence of what Timon and I are proposing is that we'd be able to make opApply's signature a union of

    void opApply(int delegate(ref T) dg)
    void opApply(int delegate(ref immutable(T)) dg) immutable
    void opApply(int delegate(ref const(T)) dg) const

or, to generalise a bit, declare a function whose signature is a union of

    void func(ref T param, int delegate(ref T) dg)
    void func(ref immutable(T) param, int delegate(ref immutable(T)) dg)
    void func(ref const(T) param, int delegate(ref const(T)) dg)

or similarly with pointer or array parameters.

> It would be ideal for inout to solve this, but it's not chartered in such a way to do so.

If I'm not mistaken, inout isn't chartered to do anything particular when it occurs in a function signature nested within another.  The spec just leaves it ambiguous.

> It's currently transitive, and this would break transitivity. If we want to look at
> fundamentally redefining inout so that it can break transitivity, then we can look at
> that. But I don't think this is a simple "add-on" to the current functionality.
<snip>

Can you give an example of how it breaks transitivity?

Stewart.
« First   ‹ Prev
1 2 3