www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - Out parameters and initialization

reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
The spec says:

out parameters are set to the default initializer for the type of it.

Which makes perfect sense, and works just fine.  The trick comes in when 
I try some parameter initialization.  This really weirded me out at 
first.  Example:

int main()
{
         uint i = 0;
         test(i);

         return 0;
}

void test(out uint i = 5)
{
         assert(i == 5);
}

It seems to me that I should either get an error from the compiler, or 
this should compile and run without any assertions failing.  However, 
the assert on line 11 does not pass, as of DMD 0.147.

Either way, I would suggest the sentence in the spec be clarified (and 
the grammar buffed), for example:

out parameters are always set to the default initializer for their type. 
  Using an initializer on an out parameter is an error.

Or:

out parameters are always set to the default initializer for their type, 
unless they have an explicit initializer (but regardless of the 
parameters initial value.)

I couldn't find this reported before, but if it has been please forgive 
the duplicate.

Hope that's helpful,
-[Unknown]
Feb 19 2006
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Unknown W. Brackets schrieb am 2006-02-20:
 The spec says:

 out parameters are set to the default initializer for the type of it.

 Which makes perfect sense, and works just fine.  The trick comes in when 
 I try some parameter initialization.  This really weirded me out at 
 first.  Example:

 int main()
 {
          uint i = 0;
          test(i);

          return 0;
 }

 void test(out uint i = 5)
 {
          assert(i == 5);
 }

 It seems to me that I should either get an error from the compiler, or 
 this should compile and run without any assertions failing.  However, 
 the assert on line 11 does not pass, as of DMD 0.147.

 Either way, I would suggest the sentence in the spec be clarified (and 
 the grammar buffed), for example:

 out parameters are always set to the default initializer for their type. 
   Using an initializer on an out parameter is an error.

 Or:

 out parameters are always set to the default initializer for their type, 
 unless they have an explicit initializer (but regardless of the 
 parameters initial value.)

Where exactly are default parameters for functions documented? changelog: | Added default arguments to function parameters. Semantics are like C++. functions: | A functions parameter's default value is not inherited. C++ has - as far as I'm aware - no out keyword. Added to DStress as http://dstress.kuehne.cn/undefined/default_argument_10.d Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFD/rXF3w+/yD4P9tIRAnagAJ49GTZBvwbg4gQG3x2vweQsCJsTbwCguviK tP/CaKTY4XUpBuCgkyI0Xu8= =mkMH -----END PGP SIGNATURE-----
Feb 23 2006
parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
I may have been unclear, but this is the thing I was most specifically 
referencing; that there was no defined behavior for this in the spec's 
wording.

As for default parameters (initializers) being defined at all, that's in 
the declarations page:

Parameter:
         Declarator
         Declarator = AssignExpression
         InOut Declarator
         InOut Declarator = AssignExpression

This presumably implies it has the same semantics as 
DeclaratorInitializer, but there's no explicit definition.  Further, it 
is interesting to me that AssignExpression is used instead of 
Initializer; this may actually mean different semantics (and not just 
that you cannot use array/struct literals and void here.)

-[Unknown]


 Where exactly are default parameters for functions documented?

Feb 24 2006
prev sibling next sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 The spec says:
 
 out parameters are set to the default initializer for the type of it.
 
 Which makes perfect sense, and works just fine.  The trick comes in when 
 I try some parameter initialization.  This really weirded me out at 
 first.  Example:
 
 int main()
 {
         uint i = 0;
         test(i);
 
         return 0;
 }
 
 void test(out uint i = 5)
 {
         assert(i == 5);
 }
 

Oops, missed this thread before. Actually it kind of makes sence to me that this doesn't work. But this does work, and is IMO a great feature: int globalI = 0; int main() { uint i = 0; test(); //globalI is now 5 return 0; } void test(out uint i = globalI) { i = 5; assert(i == 5); }
Feb 24 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Where is that in the spec?  Why is that logical?

-[Unknown]


 Unknown W. Brackets wrote:
 The spec says:

 out parameters are set to the default initializer for the type of it.

 Which makes perfect sense, and works just fine.  The trick comes in 
 when I try some parameter initialization.  This really weirded me out 
 at first.  Example:

 int main()
 {
         uint i = 0;
         test(i);

         return 0;
 }

 void test(out uint i = 5)
 {
         assert(i == 5);
 }

Oops, missed this thread before. Actually it kind of makes sence to me that this doesn't work. But this does work, and is IMO a great feature: int globalI = 0; int main() { uint i = 0; test(); //globalI is now 5 return 0; } void test(out uint i = globalI) { i = 5; assert(i == 5); }

Feb 24 2006
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Where is that in the spec?  Why is that logical?

In the spec? As far as I know it isn't (at least I didn't see it anywhere). Logical because: void func(in int x = 7); means this is an input value and if I don't suply one the default value will be 7. void func(out int x = globalX); out tels us the value of x will be changed, the default value says if I don't suply an argument change by default the value of variable globalX. If you did something like: void func(out int x = 7) { x = 11; } the compiler will complain about 7 not being an l-value. It is the same reason why you can't call the above function like func(11); 11 can not be assigned to. The above effect can be/(should be able to be) achieved by typedef int int7 = 7; or something like this. I hope this is the way it is ment to work and not some crazy bug because I'm really starting to like this feature.
 

Feb 25 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Honestly, I understand the usefulness and where you're coming from, but 
if that's really intended it makes absolutely no logical sense to me.

That would be like this working:

int i = 0;
int j = i;

j = 5;
assert(i == 5);

I realize that out is a reference/pointer type thing (automatically), 
but if there's no * there or anything, I don't want other variables to 
change.  It completely contradicts everything else in the language to 
me.... maybe it's just me, though.

-[Unknown]


 Unknown W. Brackets wrote:
 Where is that in the spec?  Why is that logical?

In the spec? As far as I know it isn't (at least I didn't see it anywhere). Logical because: void func(in int x = 7); means this is an input value and if I don't suply one the default value will be 7. void func(out int x = globalX); out tels us the value of x will be changed, the default value says if I don't suply an argument change by default the value of variable globalX. If you did something like: void func(out int x = 7) { x = 11; } the compiler will complain about 7 not being an l-value. It is the same reason why you can't call the above function like func(11); 11 can not be assigned to. The above effect can be/(should be able to be) achieved by typedef int int7 = 7; or something like this. I hope this is the way it is ment to work and not some crazy bug because I'm really starting to like this feature.


Feb 25 2006
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Honestly, I understand the usefulness and where you're coming from, but 
 if that's really intended it makes absolutely no logical sense to me.

And I honestly understand what behaviour you want. It happens that I accidently found this behaviour and find it usefull.
 
 That would be like this working:
 
 int i = 0;
 int j = i;
 
 j = 5;
 assert(i == 5);

Well with this I don't agree. int here isn't a reference type. (And I know that you didn't really mean this as an equivalent example)
 
 I realize that out is a reference/pointer type thing (automatically), 
 but if there's no * there or anything, I don't want other variables to 
 change.  It completely contradicts everything else in the language to 
 me.... maybe it's just me, though.

I see it like this (maybe I'm repeating my self) void func(in int a = 5) //func(), use default in value of 5 Default values are ment for cases when *no arguments* are specified: void func(out int a = 7) { a = 3; } func(); //what is the above code doing? Assigning 3 to what? To 7? That isn't possible because 7 is a constant. The idea that a function void func(out int a = 7){} when called like this: func(b); is assigning 7 to b, contradicts the meaning of default in arguments.
Feb 25 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
I understand it is useful.  Indeed, the following, if it worked, could 
possibly also be useful:

label:
// Never executed.
x = 1;

comefrom label;

That does not make it logical or consistent.  It makes it confusing and 
inconsistent.  Utility is not at all my concern.

In other words, you're essentially saying that this makes sense:

var2 = 2;
var1 = var2;
var1 = 1;
assert(var2 == 1);

If it were the other way around (var2 = var1) or if either variable had 
anything to do with C-style references or pointers, I might agree with 
you.  But even if they were classes, this would make no sense whatsoever.

It simply is not consistent with the rest of the language.  If it's 
going to work that way, it should do so everywhere.

-[Unknown]


 Unknown W. Brackets wrote:
 Honestly, I understand the usefulness and where you're coming from, 
 but if that's really intended it makes absolutely no logical sense to me.

And I honestly understand what behaviour you want. It happens that I accidently found this behaviour and find it usefull.
 That would be like this working:

 int i = 0;
 int j = i;

 j = 5;
 assert(i == 5);

Well with this I don't agree. int here isn't a reference type. (And I know that you didn't really mean this as an equivalent example)
 I realize that out is a reference/pointer type thing (automatically), 
 but if there's no * there or anything, I don't want other variables to 
 change.  It completely contradicts everything else in the language to 
 me.... maybe it's just me, though.

I see it like this (maybe I'm repeating my self) void func(in int a = 5) //func(), use default in value of 5 Default values are ment for cases when *no arguments* are specified: void func(out int a = 7) { a = 3; } func(); //what is the above code doing? Assigning 3 to what? To 7? That isn't possible because 7 is a constant. The idea that a function void func(out int a = 7){} when called like this: func(b); is assigning 7 to b, contradicts the meaning of default in arguments.

Feb 25 2006
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 25 Feb 2006 16:21:23 -0800, Unknown W. Brackets  
<unknown simplemachines.org> wrote:
 That does not make it logical or consistent.  It makes it confusing and  
 inconsistent.  Utility is not at all my concern.

Ok, so lets look at the consistency of this.
 In other words, you're essentially saying that this makes sense:

 var2 = 2;
 var1 = var2;
 var1 = 1;
 assert(var2 == 1);

No, he's saying that this makes sense: void foo(inout int var1) { var1 = 1; } int var2 = 2; foo(var2); assert(var2 == 1); Remember, 'inout' aliases the variable you pass, the parameter 'var1' is another name for the variable 'var2'. So, when you write: void foo(inout int var1 = 7) what are you saying? Lets start with what a default parameters means, in this case: void foo(int var1 = 7) {} says, if I do not get passed a variable, pass 7, in other words: foo(); is called as: foo(7); Agreed? Therefore: void foo(inout int var1 = 7) says, if I do not get passed a variable, pass 7, meaning: foo(); is called as: foo(7); Agreed? Now, as well all know you cannot pass '7' as inout because you cannot alias '7'. What I am trying to show here is that default parameters are behaving both logically and consistently, they always mean: "If I do not get passed a variable, pass X" where X is the default parameter value supplied. Further, 'inout' is behaving both logically and consistently, in all cases it aliases the parameter which is passed. The behaviour of 'in'+'default parameter' is not the same as 'inout'+'default parameter' but why should it be? IMO one is an apple and the other an orange. Regan
Feb 25 2006
next sibling parent "Derek Parnell" <derek psych.ward> writes:
On Sun, 26 Feb 2006 12:56:04 +1100, Regan Heath <regan netwin.co.nz> wrote:

 The behaviour of 'in'+'default parameter' is not the same as  
 'inout'+'default parameter' but why should it be? IMO one is an apple  
 and the other an orange.

This makes a lot of sense to me. -- Derek Parnell Melbourne, Australia
Feb 25 2006
prev sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
If that is the meaning, some symbol should be used to indicate that 
there is a reference being made.

I don't think, personally, it makes any sense to say that in is a 
replacement for what might be passed.  It is much more like an 
initializer, which would never behave in that way.  To me, it is unclear 
and unlike the rest of the language to do as you suggest.

Your suggestion separates the logic of parameter defaulting from 
initialization completely, but they share the same syntax (as they share 
again with assignment.)  I think this is, at best, unclear and clumsy. 
At worst, it is obviously inconsistent and confusing.

But that is more of a philosophical debate, and that will be for Walter 
to answer, ultimately.  My point is that there is a bug here, nonetheless.

Regardless of what you are saying, why should my initial example 
compile?  It should either work as initialization, or not compile.  It 
should not compile and do nothing.  This is also unclear and inconsistent.

-[Unknown]


 On Sat, 25 Feb 2006 16:21:23 -0800, Unknown W. Brackets 
 <unknown simplemachines.org> wrote:
 That does not make it logical or consistent.  It makes it confusing 
 and inconsistent.  Utility is not at all my concern.

Ok, so lets look at the consistency of this.
 In other words, you're essentially saying that this makes sense:

 var2 = 2;
 var1 = var2;
 var1 = 1;
 assert(var2 == 1);

No, he's saying that this makes sense: void foo(inout int var1) { var1 = 1; } int var2 = 2; foo(var2); assert(var2 == 1); Remember, 'inout' aliases the variable you pass, the parameter 'var1' is another name for the variable 'var2'. So, when you write: void foo(inout int var1 = 7) what are you saying? Lets start with what a default parameters means, in this case: void foo(int var1 = 7) {} says, if I do not get passed a variable, pass 7, in other words: foo(); is called as: foo(7); Agreed? Therefore: void foo(inout int var1 = 7) says, if I do not get passed a variable, pass 7, meaning: foo(); is called as: foo(7); Agreed? Now, as well all know you cannot pass '7' as inout because you cannot alias '7'. What I am trying to show here is that default parameters are behaving both logically and consistently, they always mean: "If I do not get passed a variable, pass X" where X is the default parameter value supplied. Further, 'inout' is behaving both logically and consistently, in all cases it aliases the parameter which is passed. The behaviour of 'in'+'default parameter' is not the same as 'inout'+'default parameter' but why should it be? IMO one is an apple and the other an orange. Regan

Feb 25 2006
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 25 Feb 2006 18:58:44 -0800, Unknown W. Brackets  
<unknown simplemachines.org> wrote:
 If that is the meaning, some symbol should be used to indicate that  
 there is a reference being made.

The symbols 'inout' and 'out' indicate an alias/reference is being made.
 I don't think, personally, it makes any sense to say that in is a  
 replacement for what might be passed.  It is much more like an  
 initializer, which would never behave in that way.  To me, it is unclear  
 and unlike the rest of the language to do as you suggest.

I didn't mean to suggest that 'in' was a replacement for what is passed, but rather that the default initialiser is a replacement for what _isn't_ passed.
 Your suggestion separates the logic of parameter defaulting from  
 initialization completely, but they share the same syntax (as they share  
 again with assignment.)  I think this is, at best, unclear and clumsy.  
 At worst, it is obviously inconsistent and confusing.

I think you make a valid point here. default parameters, and assignment (which initialization is essentially) share the same syntax, they both use '='. It does suggest that a default parameter is an initializer, when in fact, I believe it's not that at all. That same syntax, '=', is likely the cause of this thread :)
 But that is more of a philosophical debate, and that will be for Walter  
 to answer, ultimately.  My point is that there is a bug here,  
 nonetheless.

I agree, my idea at the solution is below.
 Regardless of what you are saying, why should my initial example  
 compile? should not compile, it shouldIt should either work as  
 initialization, or not compile.  It should not compile and do nothing.   
 This is also unclear and inconsistent.

Your first example compiles because you are passing a parameter to all calls of 'test' and therefore the default parameter is never used. However, this for example: void test(out uint i = 5) { assert(i == 5); } void main() { uint i; test(i); //compiles test(); //cast(uint)5 is not an lvalue } Will not compile because the default parameter is required for the 2nd call to 'test' and '5' is not an lvalue (cannot be aliased). The solution: I think it would be fair to ask for the compiler to error on: void test(out uint i = 5) regardless of whether the default parameter is ever required, simply because it will never be legal to have a non lvalue default parameter to an 'out' or 'inout' parameter. At the same time perhaps this should remain valid: uint globalVar; void test(out uint i = globalVar) {} because I can imagine some useful things you can do with this. What do you think Walter? Regan
 -[Unknown]


 On Sat, 25 Feb 2006 16:21:23 -0800, Unknown W. Brackets  
 <unknown simplemachines.org> wrote:
 That does not make it logical or consistent.  It makes it confusing  
 and inconsistent.  Utility is not at all my concern.

 In other words, you're essentially saying that this makes sense:

 var2 = 2;
 var1 = var2;
 var1 = 1;
 assert(var2 == 1);

void foo(inout int var1) { var1 = 1; } int var2 = 2; foo(var2); assert(var2 == 1); Remember, 'inout' aliases the variable you pass, the parameter 'var1' is another name for the variable 'var2'. So, when you write: void foo(inout int var1 = 7) what are you saying? Lets start with what a default parameters means, in this case: void foo(int var1 = 7) {} says, if I do not get passed a variable, pass 7, in other words: foo(); is called as: foo(7); Agreed? Therefore: void foo(inout int var1 = 7) says, if I do not get passed a variable, pass 7, meaning: foo(); is called as: foo(7); Agreed? Now, as well all know you cannot pass '7' as inout because you cannot alias '7'. What I am trying to show here is that default parameters are behaving both logically and consistently, they always mean: "If I do not get passed a variable, pass X" where X is the default parameter value supplied. Further, 'inout' is behaving both logically and consistently, in all cases it aliases the parameter which is passed. The behaviour of 'in'+'default parameter' is not the same as 'inout'+'default parameter' but why should it be? IMO one is an apple and the other an orange. Regan


Feb 25 2006
next sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Sun, 26 Feb 2006 17:30:09 +1300, Regan Heath <regan netwin.co.nz> wrote:
 I didn't mean to suggest that 'in' was a replacement for what is passed,  
 but rather that the default initialiser is a replacement for what  
 _isn't_ passed.

Oops, here I am muddying the waters, "default initialiser" should read "default parameter". Regan
Feb 25 2006
prev sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Except aliases and typedefs (exactly, as I could tell, for the reason of 
consistency) do not use an = sign.  If they did, it would be strange and 
inconsistent.  I am very glad they do not.

Personally, I think = should be disallowed entirely for out parameters, 
and should work on inout parameters in the same way as it does for in 
ones.  This would stay logical.

If out int i = 5 should not compile when no parameter is passed, it 
should not compile when a parameter is passed - as you said.  I think 
the usefulness of global variables, especially considering thread 
safety, would only grow confusing in actual and practical use.

-[Unknown]


 On Sat, 25 Feb 2006 18:58:44 -0800, Unknown W. Brackets 
 <unknown simplemachines.org> wrote:
 If that is the meaning, some symbol should be used to indicate that 
 there is a reference being made.

The symbols 'inout' and 'out' indicate an alias/reference is being made.
 I don't think, personally, it makes any sense to say that in is a 
 replacement for what might be passed.  It is much more like an 
 initializer, which would never behave in that way.  To me, it is 
 unclear and unlike the rest of the language to do as you suggest.

I didn't mean to suggest that 'in' was a replacement for what is passed, but rather that the default initialiser is a replacement for what _isn't_ passed.
 Your suggestion separates the logic of parameter defaulting from 
 initialization completely, but they share the same syntax (as they 
 share again with assignment.)  I think this is, at best, unclear and 
 clumsy. At worst, it is obviously inconsistent and confusing.

I think you make a valid point here. default parameters, and assignment (which initialization is essentially) share the same syntax, they both use '='. It does suggest that a default parameter is an initializer, when in fact, I believe it's not that at all. That same syntax, '=', is likely the cause of this thread :)
 But that is more of a philosophical debate, and that will be for 
 Walter to answer, ultimately.  My point is that there is a bug here, 
 nonetheless.

I agree, my idea at the solution is below.
 Regardless of what you are saying, why should my initial example 
 compile? should not compile, it shouldIt should either work as 
 initialization, or not compile.  It should not compile and do 
 nothing.  This is also unclear and inconsistent.

Your first example compiles because you are passing a parameter to all calls of 'test' and therefore the default parameter is never used. However, this for example: void test(out uint i = 5) { assert(i == 5); } void main() { uint i; test(i); //compiles test(); //cast(uint)5 is not an lvalue } Will not compile because the default parameter is required for the 2nd call to 'test' and '5' is not an lvalue (cannot be aliased). The solution: I think it would be fair to ask for the compiler to error on: void test(out uint i = 5) regardless of whether the default parameter is ever required, simply because it will never be legal to have a non lvalue default parameter to an 'out' or 'inout' parameter. At the same time perhaps this should remain valid: uint globalVar; void test(out uint i = globalVar) {} because I can imagine some useful things you can do with this. What do you think Walter? Regan
 -[Unknown]


Feb 25 2006
next sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Except aliases and typedefs (exactly, as I could tell, for the reason of 
 consistency) do not use an = sign.  If they did, it would be strange and 
 inconsistent.  I am very glad they do not.
 
 Personally, I think = should be disallowed entirely for out parameters, 
 and should work on inout parameters in the same way as it does for in 
 ones.  This would stay logical.
 
 If out int i = 5 should not compile when no parameter is passed, it 
 should not compile when a parameter is passed - as you said.  

No, it should never compile.
 I think 
 the usefulness of global variables, especially considering thread 
 safety, would only grow confusing in actual and practical use.

Maybe, but noone is stoping you from implementing thread safe access to global variable if thread safety is what you need.
Feb 26 2006
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 25 Feb 2006 21:02:30 -0800, Unknown W. Brackets  
<unknown simplemachines.org> wrote:
 Personally, I think = should be disallowed entirely for out parameters,  
 and should work on inout parameters in the same way as it does for in  
 ones.  This would stay logical.

You realise 'inout' aliases the parameter in the same was as 'out' does? Regan
Feb 26 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
I'm thinking from the "in-side".  In my mind, if I leave off an out or 
inout parameter, I want the result to go nowhere (but might still expect 
the variable to exist in the function.)  I don't see how this isn't 
logical, but I can understand how you'd disagree.

You do seem to disagree; to make us both happy, perhaps only in should 
have default parameters allowed.

-[Unknown]


 On Sat, 25 Feb 2006 21:02:30 -0800, Unknown W. Brackets 
 <unknown simplemachines.org> wrote:
 Personally, I think = should be disallowed entirely for out 
 parameters, and should work on inout parameters in the same way as it 
 does for in ones.  This would stay logical.

You realise 'inout' aliases the parameter in the same was as 'out' does? Regan

Feb 26 2006
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 I'm thinking from the "in-side".  In my mind, if I leave off an out or 
 inout parameter, I want the result to go nowhere (but might still expect 
 the variable to exist in the function.)  I don't see how this isn't 
 logical, but I can understand how you'd disagree.

If you don't want a result the solution is easy: void func(in int x = 7) func(); All conditions satisfied: * result is going nowhere * var exists in a function * it's value is 7 The point is: if you don't need a result, then what you probably want is in and not out/inout. Out is for something else.
Feb 26 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Obviously that has nothing to do with what I meant.

Consider:

int foo(out C x, out C y, out C z);

It is possible I may not want to specify a y or a z.  I may however 
still want the return value and x.  In other parts of my code, I may 
want x and y, or all three.  Only in some places will I not want z or y.

You clearly misunderstood me.  Using in has absolutely nothing to do 
with this.

Currently, a workaround would be:

int foo(out C x)
{
    C dummy1, dummy2;
    return foo(x, dummy1, dummy2);
}

But this really is going to the side a bit of the topic.

-[Unknown]


 Unknown W. Brackets wrote:
 I'm thinking from the "in-side".  In my mind, if I leave off an out or 
 inout parameter, I want the result to go nowhere (but might still 
 expect the variable to exist in the function.)  I don't see how this 
 isn't logical, but I can understand how you'd disagree.

If you don't want a result the solution is easy: void func(in int x = 7) func(); All conditions satisfied: * result is going nowhere * var exists in a function * it's value is 7 The point is: if you don't need a result, then what you probably want is in and not out/inout. Out is for something else.

Feb 26 2006
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Obviously that has nothing to do with what I meant.

Uh, sorry I'm tired.
 
 Consider:
 
 int foo(out C x, out C y, out C z);
 
 It is possible I may not want to specify a y or a z.  I may however 
 still want the return value and x.  In other parts of my code, I may 
 want x and y, or all three.  Only in some places will I not want z or y.
 
 You clearly misunderstood me.  Using in has absolutely nothing to do 
 with this.

I see that now:
 
 Currently, a workaround would be:
 
 int foo(out C x)
 {
    C dummy1, dummy2;
    return foo(x, dummy1, dummy2);
 }

No: a workaround would be: private int dummy1, dummy2; void foo(out int x = dummy1, out int y = dummy2, out int z = dummy3){...} int foo(out C x) { return foo(x); } It complicates library code just a little but simplifies users code.
 
 But this really is going to the side a bit of the topic.

I thought the topic was initialization of out parameters.
Feb 26 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Yes, but this is a topic about a bug.  We are no longer really talking 
about the bug.

If you used your workaround, you would have:

1. Extra global variables somewhere in memory, which may not be desirable.

2. Uninitialized variables, from what I can tell (out parameters are 
normally initialized to their default state at the beginning of the 
function.)  This may cause unexpected/hard-to-reproduce bugs.

3. Requirements to use synchronized blocks around accesses to the 
variables, which would normally not be necessary with typical out 
parameters.  This would be needed because the function may depend on the 
global variables not changing mid-function, but the function might get 
called numerous times concurrently in multiple threads.

It's true that programming involves the above all the time, but they can 
all be avoided in this case by not using your described method.  As 
such, I consider them flaws in the proposed method.

-[Unknown]


 Unknown W. Brackets wrote:
 Obviously that has nothing to do with what I meant.

Uh, sorry I'm tired.
 Consider:

 int foo(out C x, out C y, out C z);

 It is possible I may not want to specify a y or a z.  I may however 
 still want the return value and x.  In other parts of my code, I may 
 want x and y, or all three.  Only in some places will I not want z or y.

 You clearly misunderstood me.  Using in has absolutely nothing to do 
 with this.

I see that now:
 Currently, a workaround would be:

 int foo(out C x)
 {
    C dummy1, dummy2;
    return foo(x, dummy1, dummy2);
 }

No: a workaround would be: private int dummy1, dummy2; void foo(out int x = dummy1, out int y = dummy2, out int z = dummy3){...} int foo(out C x) { return foo(x); } It complicates library code just a little but simplifies users code.
 But this really is going to the side a bit of the topic.

I thought the topic was initialization of out parameters.

Feb 26 2006
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sun, 26 Feb 2006 14:44:57 -0800, Unknown W. Brackets  
<unknown simplemachines.org> wrote:
 Yes, but this is a topic about a bug.  We are no longer really talking  
 about the bug.

 If you used your workaround, you would have:

 1. Extra global variables somewhere in memory, which may not be  
 desirable.

True, however you can use the same default global for every parameter of the same type provided the 'out' contract of the function does not check them (which is something you will know and can work around), eg. int dontCare; int test(out int a = dontCare, out int b = dontCare, out int c = dontCare) { ... } You can do this because the dummy globals are never 'read' by anything at any time.
 2. Uninitialized variables, from what I can tell (out parameters are  
 normally initialized to their default state at the beginning of the  
 function.) This may cause unexpected/hard-to-reproduce bugs.

'out' and dummy globals do not prevent the init of the out parameter: import std.stdio; int global = 5; void test(out int a = global) { writefln(a); } void main() { writefln(global); test(); } Output: 5 0
 3. Requirements to use synchronized blocks around accesses to the  
 variables

Why? Given that value of an 'out' variable is never 'read' by anyone at any time it does not matter what value it has at any given time, or even that it has any sensible value at all. The same cannot be said for 'inout' however. For 'inout' you would need to protect access to the global. For 'inout' your points above are valid. You would need to protect access to the global, but, as Ivan mentioned that may be exactly what you want. In the end are we arguing about the utility of each set of behaviour or the logic of it? If we're arguing about the logic, my opinion remains that the current behaviour is logical given the following rules: "out and inout _always_ alias/reference the parameter passed" "a default parameter is passed when no parameter is given explicitly" "a constant cannot be passed by reference" In order to implement inout how you describe (as 'in' sometimes) you'd need to break rule #1, making the behaviour illogical and inconsistent with other instances of 'inout'. The problem seems mainly to be the perception that a default parameter is a default initializer and that is simply incorrect in the general case. It is only true for value types passed as 'in' parameters. Take for example: void test(int* a = 5) {} //cannot implicitly convert expression (5) of type int to int* you would never expect that to work, right? This is essentially identical to: void test(inout int a = 5) {} Regan
Feb 26 2006
parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Given the following example:

class X
{
    int bar;
}

int foo(out X var)
{
    var.bar = 5;

    assert(var.bar == 5);
}

Look at that, an out parameter - being read?  Amazing, I've done the 
unthinkable.

If a variable is a pointer, it should be a pointer.  Classes, as 
reference types, are clear because they are always pointers.  However, 
consider the following:

import std.stdio;

int main()
{
	int x;
	test(x);

	return 0;
}

void test(out int x)
{
	writefln(typeid(typeof(x)));
}

This code clearly identifies x as an int.  If it is an int, it should be 
an int, and it should always be an int.  I mean, that's just common 
sense.  If an animal is a moose, it should not be a giraffe.  We learn 
this in kindergarten.

In every other way, out and inout parameters are treated as moose.  ONLY 
when they receive a default parameter are they being treated as giraffe. 
  This is inconsistent.  They should only ever be treated as one animal: 
as a giraffe, or as a moose.  There is no girmooffe.

I've really grown quite tired with your patronizing and condescending 
tone, and you are reminding me exactly what I dislike about this 
newsgroup and therefore D; the people who seem to surround it.  The 
language seems okay but I honestly remember why I don't use it as often 
every time I get in a conversation with one of you people.

-[Unknown]


 On Sun, 26 Feb 2006 14:44:57 -0800, Unknown W. Brackets 
 <unknown simplemachines.org> wrote:
 Yes, but this is a topic about a bug.  We are no longer really talking 
 about the bug.

 If you used your workaround, you would have:

 1. Extra global variables somewhere in memory, which may not be 
 desirable.

True, however you can use the same default global for every parameter of the same type provided the 'out' contract of the function does not check them (which is something you will know and can work around), eg. int dontCare; int test(out int a = dontCare, out int b = dontCare, out int c = dontCare) { ... } You can do this because the dummy globals are never 'read' by anything at any time.
 2. Uninitialized variables, from what I can tell (out parameters are 
 normally initialized to their default state at the beginning of the 
 function.) This may cause unexpected/hard-to-reproduce bugs.

'out' and dummy globals do not prevent the init of the out parameter: import std.stdio; int global = 5; void test(out int a = global) { writefln(a); } void main() { writefln(global); test(); } Output: 5 0
 3. Requirements to use synchronized blocks around accesses to the 
 variables

Why? Given that value of an 'out' variable is never 'read' by anyone at any time it does not matter what value it has at any given time, or even that it has any sensible value at all. The same cannot be said for 'inout' however. For 'inout' you would need to protect access to the global. For 'inout' your points above are valid. You would need to protect access to the global, but, as Ivan mentioned that may be exactly what you want. In the end are we arguing about the utility of each set of behaviour or the logic of it? If we're arguing about the logic, my opinion remains that the current behaviour is logical given the following rules: "out and inout _always_ alias/reference the parameter passed" "a default parameter is passed when no parameter is given explicitly" "a constant cannot be passed by reference" In order to implement inout how you describe (as 'in' sometimes) you'd need to break rule #1, making the behaviour illogical and inconsistent with other instances of 'inout'. The problem seems mainly to be the perception that a default parameter is a default initializer and that is simply incorrect in the general case. It is only true for value types passed as 'in' parameters. Take for example: void test(int* a = 5) {} //cannot implicitly convert expression (5) of type int to int* you would never expect that to work, right? This is essentially identical to: void test(inout int a = 5) {} Regan

Feb 26 2006
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Given the following example:
 
 class X
 {
    int bar;
 }
 
 int foo(out X var)
 {
    var.bar = 5;
 
    assert(var.bar == 5);
 }
 
 Look at that, an out parameter - being read?  Amazing, I've done the 
 unthinkable.

Don't see what the problem is, you as an author of foo know what you are doing so know if there should be only one dummy variable or more.
 
 If a variable is a pointer, it should be a pointer.  

Agree.
 Classes, as 
 reference types, are clear because they are always pointers.  

Agree.
 However, 
 consider the following:
 
 import std.stdio;
 
 int main()
 {
     int x;
     test(x);
 
     return 0;
 }
 
 void test(out int x)
 {
     writefln(typeid(typeof(x)));
 }
 
 This code clearly identifies x as an int.  If it is an int, it should be 
 an int, and it should always be an int.  I mean, that's just common 
 sense.  If an animal is a moose, it should not be a giraffe.  We learn 
 this in kindergarten.

Well in this case it is a moose&. D handles references a little strange meaning they aren't really real types, but they are still here. An out parameter means int is passed by reference. You should think about it as: void test(int& x) in C++.
 
 In every other way, out and inout parameters are treated as moose.  

Not really. Can the original moose changed when you pass it as in? No. Can it be changed when you pass it as out, or inout. Yes. So they aren't really treated the same.
 I've really grown quite tired with your patronizing and condescending 
 tone, and you are reminding me exactly what I dislike about this 
 newsgroup and therefore D; the people who seem to surround it.  The 
 language seems okay but I honestly remember why I don't use it as often 
 every time I get in a conversation with one of you people.

I am sorry if anything I said seams patronizing or condescending. I can assure you I didn't mean it that way. I was just trying to explain my point of view. Maybe my imperfect English is a cause for occasional bad choise of words that can be misinterpreted. Once again. I am sorry. Maybe it is best to stop the conversation.
Feb 26 2006
parent "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 27 Feb 2006 01:41:04 +0100, Ivan Senji  
<ivan.senji_REMOVE_ _THIS__gmail.com> wrote:
 I've really grown quite tired with your patronizing and condescending  
 tone, and you are reminding me exactly what I dislike about this  
 newsgroup and therefore D; the people who seem to surround it.  The  
 language seems okay but I honestly remember why I don't use it as often  
 every time I get in a conversation with one of you people.

I am sorry if anything I said seams patronizing or condescending. I can assure you I didn't mean it that way. I was just trying to explain my point of view.

I couldn't have put it better Ivan. "[Unknown]" you have my apologies also. Regan
Feb 26 2006
prev sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Unknown W. Brackets wrote:
 Yes, but this is a topic about a bug.  We are no longer really talking 
 about the bug.

Do we agree then that the bug is that void f(out int x = 5) isn't an error?
 
 If you used your workaround, you would have:
 
 1. Extra global variables somewhere in memory, which may not be desirable.

But as Regan said, possibly only one for each type.
 
 2. Uninitialized variables, from what I can tell (out parameters are 
 normally initialized to their default state at the beginning of the 
 function.)  This may cause unexpected/hard-to-reproduce bugs.

Same happens here. void f(out int x = globalX) -> globalX is initialized to int.init. No bugs here.
 
 3. Requirements to use synchronized blocks around accesses to the 
 variables, which would normally not be necessary with typical out 
 parameters.  This would be needed because the function may depend on the 
 global variables not changing mid-function, but the function might get 
 called numerous times concurrently in multiple threads.

I agree this is a problem in multithreaded enviroment.
 
 It's true that programming involves the above all the time, but they can 
 all be avoided in this case by not using your described method.  As 
 such, I consider them flaws in the proposed method.

But there isn't any other way to solve what you wanted? Or is there? I mean wanting not allways to pass out arguments to a function. Atleast not other way without changing the language so that out default params are made strange and inconsistent. But I agree this discussion sholud probably end soon :)
Feb 26 2006
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 19 Feb 2006 17:53:45 -0800, Unknown W. Brackets wrote:

 The spec says:
 
 out parameters are set to the default initializer for the type of it.
 
 Which makes perfect sense, and works just fine.  The trick comes in when 
 I try some parameter initialization. 

I'm writing this without reading the entire thread, so please excuse me if I'm out of line. I also preface these remarks by noting that the documentation is exceedingly light on the whole 'default parameter' specification. Functions: "A function parameter's default value is not inherited ..." Change log for v0.92: "Added default arguments to function parameters. Semantics are like C++." Anyhow, it seems that you are thinking in terms of "initialize" rather than "default". The difference is that "default" (which is what I believe D is implementing) is invoked by the compiler when a omit a parameter on the call, and "initialize" is when the compiler applies a value to a parameter which you have not omitted, and I don't think the D has implemented this concept. foo(in int a) // foo gets (a copy) the value of 'a'. foo(in int a = 5) // foo gets 'a' if you pass it, // otherwise it gets the literal 5 foo(out int a) // foo gets (a reference to) the value of 'a' and // sets the value of 'a' to the default initializer for // for 'int'. foo(out int a = 5) // foo gets a reference to 'a' if you pass it etc..., // otherwise it gets a reference to the literal 5 // and sets the value of 5 to 0 // (which doesn't make sense to me) foo(inout int a) // foo gets (a reference to) the value of 'a'. foo(inout int a = 5) // foo gets a reference to 'a' if you pass it, // otherwise it gets a reference to the literal 5 // (which doesn't make sense to me) So I think that defining default parameters for 'out' and 'inout' arguments should be a compile-time error, because in these cases the called program will be receiving the address of a literal. Can Walter clarify the intent of the default parameter syntax when used in conjunction with 'out' and 'inout'? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 27/02/2006 1:38:08 PM
Feb 26 2006
parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Derek Parnell wrote:
 On Sun, 19 Feb 2006 17:53:45 -0800, Unknown W. Brackets wrote:
 
 
The spec says:

out parameters are set to the default initializer for the type of it.

Which makes perfect sense, and works just fine.  The trick comes in when 
I try some parameter initialization. 

I'm writing this without reading the entire thread, so please excuse me if I'm out of line.

Who in his right mind would read the entire thread. :)
 
 I also preface these remarks by noting that the documentation is
 exceedingly light on the whole 'default parameter' specification.
 
 Functions:
   "A function parameter's default value is not inherited ..."
 
  Change log for v0.92:
   "Added default arguments to function parameters. Semantics are like C++."
 
 Anyhow, it seems that you are thinking in terms of "initialize" rather than
 "default". The difference is that "default" (which is what I believe D is
 implementing) is invoked by the compiler when a omit a parameter on the
 call, and "initialize" is when the compiler applies a value to a parameter
 which you have not omitted, and I don't think the D has implemented this
 concept.
 

Correct.
  foo(in int a) // foo gets (a copy) the value of 'a'.
  foo(in int a = 5) // foo gets 'a' if you pass it, 
                    // otherwise it gets the literal 5
 
  foo(out int a) // foo gets (a reference to) the value of 'a' and
                 // sets the value of 'a' to the default initializer for
                 // for 'int'.
  foo(out int a = 5) // foo gets a reference to 'a' if you pass it etc..., 
                    // otherwise it gets a reference to the literal 5
                    // and sets the value of 5 to 0
                    // (which doesn't make sense to me)

I must agree this doesn't make sense. But this does: foo(out int a = globalA) //if you pass something a reference to it is passed //if you don't a reference toa global int 'globalA' is passed This (to me) makes sense, is usefull, and is implemented.
 
 
  foo(inout int a) // foo gets (a reference to) the value of 'a'.
  foo(inout int a = 5) // foo gets a reference to 'a' if you pass it, 
                    // otherwise it gets a reference to the literal 5
                    // (which doesn't make sense to me)
 
 So I think that defining default parameters for 'out' and 'inout' arguments
 should be a compile-time error, because in these cases the called program
 will be receiving the address of a literal.

I think constant arguments for default out/inout arguments shuld be an error, non constant don't need to be.
 
 Can Walter clarify the intent of the default parameter syntax when used in
 conjunction with 'out' and 'inout'?
 

This would be a good thing.
Feb 26 2006