www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Array as an argument, ambiguous behaviour.

reply "Cooler" <kulkin hotbox.ru> writes:
Consider 3 functions taking array as an argument:

void fun1(in  int[] x){...}
void fun2(ref int[] x){...}
void fun3(    int[] x){...}

auto a = new int[10];

fun1(a); // Guaranteed that "a" will not be changed
fun2(a); // Guaranteed that we will see any change to "a", made 
in fun2()
fun3(a); // Changes to "a" in fun3() may be or may be not visible 
to the caller

In case of fun3() we have ambiguous behaviour, depending on the 
body of the function.

Am I right?
Is that intentional?
Jan 29 2014
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 10:55:57 UTC, Cooler wrote:
 Consider 3 functions taking array as an argument:

 void fun1(in  int[] x){...}
 void fun2(ref int[] x){...}
 void fun3(    int[] x){...}

 auto a = new int[10];

 fun1(a); // Guaranteed that "a" will not be changed
 fun2(a); // Guaranteed that we will see any change to "a", made 
 in fun2()
 fun3(a); // Changes to "a" in fun3() may be or may be not 
 visible to the caller

 In case of fun3() we have ambiguous behaviour, depending on the 
 body of the function.

 Am I right?
 Is that intentional?
Arrays are a pair of pointer and length. fun1 marks the pointer, the length and the data pointed as const. fun3 marks nothing const, but since pointer and length are passed by value, you'll only see changes to the content of x. fun2 is like fun3, but pointer and length are itself passed by reference.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
Thank you for detailed explanation. But the question is - "Is 
that correct that language allows ambiguous behavior?"
Jan 29 2014
next sibling parent "simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - "Is 
 that correct that language allows ambiguous behavior?"
Could you expand your example? fun(int[] a) {} is passing a by value, that is, the pointer and length is copied over to fun. Any changes to the elements of a will be visible, but if you reassign a directly or indirectly (expanding it so it needs to be copied to a new memory location), the changes are not visible.
Jan 29 2014
prev sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - "Is 
 that correct that language allows ambiguous behavior?"
Where is it ambiguous?
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Wednesday, 29 January 2014 at 12:40:00 UTC, Tobias Pankrath 
wrote:
 On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - "Is 
 that correct that language allows ambiguous behavior?"
Where is it ambiguous?
Ambiguity is here... When I call fun1() or fun2() I know the behavior directly from function signature (read the comments in my first post). For fun3() case the caller side don't know the behavior directly from function signature. To know what will happen with array "a", the caller must see to fun3() body. Ambiguity is - in first and second cases the caller knows what happens with "a", but in third case the caller does not know what happens with "a".
Jan 29 2014
next sibling parent reply "simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 29 January 2014 at 13:15:30 UTC, Cooler wrote:
 On Wednesday, 29 January 2014 at 12:40:00 UTC, Tobias Pankrath 
 wrote:
 On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - "Is 
 that correct that language allows ambiguous behavior?"
Where is it ambiguous?
Ambiguity is here... When I call fun1() or fun2() I know the behavior directly from function signature (read the comments in my first post). For fun3() case the caller side don't know the behavior directly from function signature. To know what will happen with array "a", the caller must see to fun3() body. Ambiguity is - in first and second cases the caller knows what happens with "a", but in third case the caller does not know what happens with "a".
'in' is a shorthard for 'const scope'. 'const' means that the callee cannot change the passed in parameter in any way, including anything available through that type (for example x[0]) 'scope' means it cannot leave the scope of the callee - that it cannot be stored away anywhere. 'ref' means that the parameter is passed by reference, and thus might be reassigned or changed in any way. No parameters means that it's mutable, but the reference cannot change. I don't see any ambiguities here.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Wednesday, 29 January 2014 at 13:38:22 UTC, simendsjo wrote:
 On Wednesday, 29 January 2014 at 13:15:30 UTC, Cooler wrote:
 On Wednesday, 29 January 2014 at 12:40:00 UTC, Tobias Pankrath 
 wrote:
 On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - 
 "Is that correct that language allows ambiguous behavior?"
Where is it ambiguous?
Ambiguity is here... When I call fun1() or fun2() I know the behavior directly from function signature (read the comments in my first post). For fun3() case the caller side don't know the behavior directly from function signature. To know what will happen with array "a", the caller must see to fun3() body. Ambiguity is - in first and second cases the caller knows what happens with "a", but in third case the caller does not know what happens with "a".
'in' is a shorthard for 'const scope'. 'const' means that the callee cannot change the passed in parameter in any way, including anything available through that type (for example x[0]) 'scope' means it cannot leave the scope of the callee - that it cannot be stored away anywhere. 'ref' means that the parameter is passed by reference, and thus might be reassigned or changed in any way. No parameters means that it's mutable, but the reference cannot change. I don't see any ambiguities here.
Don't anybody understand me?! I know what means "in" and "ref" qualifiers!
 No parameters means that it's mutable, but the reference cannot 
 change.
Here is ambiguity. void fun3(int[] x){ x ~= 5; ... } auto a = new int[10]; fun3(a); // Here content of "a" may be changed or may be not changed. Depends on the buffer size that system will allocate for "a" array.
Jan 29 2014
parent reply "Dicebot" <public dicebot.lv> writes:
On Wednesday, 29 January 2014 at 14:34:54 UTC, Cooler wrote:
 Here is ambiguity.
 void fun3(int[] x){ x ~= 5; ... }
 auto a = new int[10];
 fun3(a); // Here content of "a" may be changed or may be not 
 changed. Depends on the buffer size that system will allocate 
 for "a" array.
You use very subjective meaning for "ambiguity", one that is never normally used in programming language context. Normally term "ambiguity" is applied only when _compiler_ can't reliably decide meaning of the code snippet and needs to resort to special casing. You are correct in notion that you can't make any reasonable judgement yourself about content of a after `fun3` call but same applies to `fun2` - it can be or not be modified. It is expected - to express all semantics of function body in function signature one would have needed to make it of comparable length and complexity. In practice you need always to assume the worst - everything is modified and nothing can be trusted unless explicitly qualified other way around.
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 12:47:56 UTC, Dicebot wrote:
 On Wednesday, 29 January 2014 at 14:34:54 UTC, Cooler wrote:
 Here is ambiguity.
 void fun3(int[] x){ x ~= 5; ... }
 auto a = new int[10];
 fun3(a); // Here content of "a" may be changed or may be not 
 changed. Depends on the buffer size that system will allocate 
 for "a" array.
You use very subjective meaning for "ambiguity", one that is never normally used in programming language context. Normally term "ambiguity" is applied only when _compiler_ can't reliably decide meaning of the code snippet and needs to resort to special casing. You are correct in notion that you can't make any reasonable judgement yourself about content of a after `fun3` call but same applies to `fun2` - it can be or not be modified. It is expected - to express all semantics of function body in function signature one would have needed to make it of comparable length and complexity.
If I use fun2() I expect that fun2() will change the content of my array, and all changes I will see. If I don't want any change to my array, I will use fun1(). What should I want to use fun3()? As many people writes to me in the post - "You may want that fun3() change content of an array, but can't change length of array". I cannot imagine such use case. void fun3(int[] x){ x[0] = 5; x ~= 6; writeln(x); } // What caller will get? auto a = new int[10]; fun3(a); // What I must want here to call fun3(). If I want fun3() will change my array, I should prefer fun2() variant which will guarantee me that I will get all changes. If i just want send array to fun() for it internal use I should prefer fun1(). If I call fun3() the contents of array after the call is not predictable - it can be changed or may not. What must push me to use fun3()?
 In practice you need always to assume the worst - everything is 
 modified and nothing can be trusted unless explicitly qualified 
 other way around.
In practice if function declared as fun(in int[] a){} I can be sure that call fun(x) will not change 'x', until the compiler has a bug, or fun() author don't play with c-like pointers.
Jan 30 2014
parent reply "Dicebot" <public dicebot.lv> writes:
On Thursday, 30 January 2014 at 13:42:53 UTC, Cooler wrote:
 If I use fun2() I expect that fun2() will change the content of 
 my array, and all changes I will see. If I don't want any 
 change to my array, I will use fun1(). What should I want to 
 use fun3()?
For changes to content of array but not array itself.
Jan 30 2014
next sibling parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Thursday, 30 January 2014 at 14:40:36 UTC, Dicebot wrote:
 On Thursday, 30 January 2014 at 13:42:53 UTC, Cooler wrote:
 What should I want to use fun3()?
For changes to content of array but not array itself.
For zillion+nth time :)
Jan 30 2014
prev sibling parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 14:40:36 UTC, Dicebot wrote:
 On Thursday, 30 January 2014 at 13:42:53 UTC, Cooler wrote:
 If I use fun2() I expect that fun2() will change the content 
 of my array, and all changes I will see. If I don't want any 
 change to my array, I will use fun1(). What should I want to 
 use fun3()?
For changes to content of array but not array itself.
I agree. I just want that the case can be expressed in language syntax more obvious - something like "fun(int[] const x){}" to emphasize that I understand that fun() can change content of array, and cannot change the {pointer,size} pair.
Jan 30 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 10:24:14 -0500, Cooler <kulkin hotbox.ru> wrote:

 On Thursday, 30 January 2014 at 14:40:36 UTC, Dicebot wrote:
 On Thursday, 30 January 2014 at 13:42:53 UTC, Cooler wrote:
 If I use fun2() I expect that fun2() will change the content of my  
 array, and all changes I will see. If I don't want any change to my  
 array, I will use fun1(). What should I want to use fun3()?
For changes to content of array but not array itself.
I agree. I just want that the case can be expressed in language syntax more obvious - something like "fun(int[] const x){}" to emphasize that I understand that fun() can change content of array, and cannot change the {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 15:29:50 UTC, Steven 
Schveighoffer wrote:
 On Thu, 30 Jan 2014 10:24:14 -0500, Cooler <kulkin hotbox.ru> 
 wrote:

 On Thursday, 30 January 2014 at 14:40:36 UTC, Dicebot wrote:
 On Thursday, 30 January 2014 at 13:42:53 UTC, Cooler wrote:
 If I use fun2() I expect that fun2() will change the content 
 of my array, and all changes I will see. If I don't want any 
 change to my array, I will use fun1(). What should I want to 
 use fun3()?
For changes to content of array but not array itself.
I agree. I just want that the case can be expressed in language syntax more obvious - something like "fun(int[] const x){}" to emphasize that I understand that fun() can change content of array, and cannot change the {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'? In your case: void fun(int[] x){ x = [1, 2]; } // Compilation ok. Implementation's error. The fun() implementer made error and think that caller will get new array. But it will get it only at runtime! If we for example (just for example) have void fun(int[] const x){ x = [1, 2]; } // Compilation error.
Jan 30 2014
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() can 
 change content of array, and cannot change the {pointer,size} 
 pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias Pankrath 
wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() can 
 change content of array, and cannot change the 
 {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 15:59:48 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias Pankrath 
 wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() can 
 change content of array, and cannot change the 
 {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Sorry!!! My mistake. The caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] :)
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 16:01:32 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:59:48 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias Pankrath 
 wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() can 
 change content of array, and cannot change the 
 {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Sorry!!! My mistake. The caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] :)
But that is the proof. The intention of fun() implementer will not be achieved. And such misunderstanding will appear only at runtime.
Jan 30 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 11:04:44 -0500, Cooler <kulkin hotbox.ru> wrote:

 On Thursday, 30 January 2014 at 16:01:32 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:59:48 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias Pankrath wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in language  
 syntax more obvious - something like "fun(int[] const x){}" to  
 emphasize that I understand that fun() can change content of  
 array, and cannot change the {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Sorry!!! My mistake. The caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] :)
But that is the proof. The intention of fun() implementer will not be achieved. And such misunderstanding will appear only at runtime.
void foo(int x) { x = 5; } "hey, why doesn't that work! Setting a parameter to another value should be illegal!" -Steve
Jan 30 2014
next sibling parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 16:18:33 UTC, Steven 
Schveighoffer wrote:
 On Thu, 30 Jan 2014 11:04:44 -0500, Cooler <kulkin hotbox.ru> 
 wrote:

 On Thursday, 30 January 2014 at 16:01:32 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:59:48 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias 
 Pankrath wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() 
 can change content of array, and cannot change the 
 {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Sorry!!! My mistake. The caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] :)
But that is the proof. The intention of fun() implementer will not be achieved. And such misunderstanding will appear only at runtime.
void foo(int x) { x = 5; } "hey, why doesn't that work! Setting a parameter to another value should be illegal!" -Steve
Please understand - I am not against void foo(int[] x){} I am for predictability of behavior. You suggest to describe function's behavior in documentation - quotation from your article "It is a good idea to note in the documentation how the passed in slice might or might not be overwritten." My idea is that all potential errors must be detected as soon as possible. The D principle - "The program compile and runs as expected, or not compile at all". If you really need to call function that can change content of an array, but cannot change size of an array the language syntax should allow express it in function signature. I consider "void fun(int[] const x){}" more error prone than "void fun(int[] x){}" and for the caller and for implemeter.
 I see very little value in that. We don't need to obliterate a 
 tremendous  amount of slice usage (not mentioning how much code 
 will have to be  updated) in order to help newbies understand 
 how slices work.
Any idea can be rejected by this sentence.
Jan 30 2014
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 11:48:50 -0500, Cooler <kulkin hotbox.ru> wrote:

 Please understand - I am not against void foo(int[] x){}
From an earlier post by you:
 May be just prohibit at language level the case of fun3() function, to  
 do not allow unpredictable behavior?
I thought that this meant you were against it?
 I am for predictability of behavior. You suggest to describe function's  
 behavior in documentation - quotation from your article "It is a good  
 idea to note in the documentation how the passed in slice might or might  
 not be
 overwritten." My idea is that all potential errors must be detected as  
 soon as possible.
You cannot eradicate all errors. The intentions of a function are not apparent to the compiler. Maybe the intention is to use the argument as a buffer, and the caller should not care what happens to the buffer inside the function. Adding yet another attribute is going to increase language complexity for almost no benefit. It does not guarantee unambiguity because you have no idea what the author of the function is going to do.
 The D principle - "The program compile and runs as expected, or not  
 compile at all".
This is a fantasy. The compiler cannot know what you expect.
 If you really need to call function that can change content of an array,  
 but cannot change size of an array the language syntax should allow  
 express it in function signature. I consider "void fun(int[] const x){}"  
 more error prone than "void fun(int[] x){}" and for the caller and for  
 implemeter.
Not sure if something is mixed up there. I think void fun(int[] x) is sufficient to describe what you say. The function cannot alter x's array bounds at all, and can alter it's data.
 I see very little value in that. We don't need to obliterate a  
 tremendous  amount of slice usage (not mentioning how much code will  
 have to be  updated) in order to help newbies understand how slices  
 work.
Any idea can be rejected by this sentence.
No, only ideas that force people to change millions of lines of code, and provide scant benefits instead of taking 5 minutes to explain "no, just use x[0..2] = [1, 2]" or "just use ref int[] x" depending on the goal. -Steve
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 Please understand - I am not against void foo(int[] x){}
From an earlier post by you:
 May be just prohibit at language level the case of fun3() 
 function, to do not allow unpredictable behavior?
I thought that this meant you were against it?
That was the question with "May be..." at the beginning.
 I am for predictability of behavior. You suggest to describe 
 function's behavior in documentation - quotation from your 
 article "It is a good idea to note in the documentation how 
 the passed in slice might or might not be
 overwritten." My idea is that all potential errors must be 
 detected as soon as possible.
You cannot eradicate all errors. The intentions of a function are not apparent to the compiler. Maybe the intention is to use the argument as a buffer, and the caller should not care what happens to the buffer inside the function. Adding yet another attribute is going to increase language complexity for almost no benefit. It does not guarantee unambiguity because you have no idea what the author of the function is going to do.
 The D principle - "The program compile and runs as expected, 
 or not compile at all".
This is a fantasy. The compiler cannot know what you expect.
The language is needed to express your intentions to the compiler. The language should be expressive as possible. My point is to push programmers to write correct software. I just ask these forum to think about topic. If everybody satisfied by void fun(int[] x){} behavior, then I just go... But I encounter a bug in my program that was due to my misusing of such signature.
 If you really need to call function that can change content of 
 an array, but cannot change size of an array the language 
 syntax should allow express it in function signature. I 
 consider "void fun(int[] const x){}" more error prone than 
 "void fun(int[] x){}" and for the caller and for implemeter.
Not sure if something is mixed up there. I think void fun(int[] x) is sufficient to describe what you say. The function cannot alter x's array bounds at all, and can alter it's data.
Here is the reason why i post the topic on this forum. You think one way, I think another way. I wanted to discuss what think other people. But looks like still nobody can understand my point, or may be I cannot express it...
 I see very little value in that. We don't need to obliterate 
 a tremendous  amount of slice usage (not mentioning how much 
 code will have to be  updated) in order to help newbies 
 understand how slices work.
Any idea can be rejected by this sentence.
No, only ideas that force people to change millions of lines of code, and provide scant benefits instead of taking 5 minutes to explain "no, just use x[0..2] = [1, 2]" or "just use ref int[] x" depending on the goal. -Steve
Jan 30 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 12:38:57 -0500, Cooler <kulkin hotbox.ru> wrote:


 The D principle - "The program compile and runs as expected, or not  
 compile at all".
This is a fantasy. The compiler cannot know what you expect.
The language is needed to express your intentions to the compiler.
Anything that the compiler cannot enforce is just documentation. Does it matter whether the documentation is in a comment or part of the signature?
 The language should be expressive as possible. My point is to push  
 programmers to write correct software. I just ask these forum to think  
 about topic. If everybody satisfied by void fun(int[] x){} behavior,  
 then I just go...
I'm not trying to be a bully or anything, but you are not understanding that what you want is a guarantee of logic, that the compiler cannot possibly enforce. Your example (setting a variable to another value) being rejected is easy to work around, just use another variable that doesn't have that restriction. There are so many examples of behavior-enforcing features that result in something that the creator of the feature didn't want or expect. Keep in mind there are years of projects that have been written assuming that you can use a standard slice in the signature of a function, we don't want to invalidate all that code just because programmers sometimes write buggy code.
 But I encounter a bug in my program that was due to my misusing of such  
 signature.
Here's where the gray area is. If you make this error once or twice, but never again, is it worth changing the language over? Probably not. But if you make this error every day, even KNOWING what will happen, it's worth looking into. But any changes have to avoid a negative impact on existing code as much as possible. Think of the person who knows how slices work, who very seldom makes this mistake, and who now has to go through all his code and make this change, never finding an instance where it makes a difference. At this point, I'm not sure what you are proposing, but I don't think there is much value in changing the way slice passing works.
 If you really need to call function that can change content of an  
 array, but cannot change size of an array the language syntax should  
 allow express it in function signature. I consider "void fun(int[]  
 const x){}" more error prone than "void fun(int[] x){}" and for the  
 caller and for implemeter.
Not sure if something is mixed up there. I think void fun(int[] x) is sufficient to describe what you say. The function cannot alter x's array bounds at all, and can alter it's data.
Here is the reason why i post the topic on this forum. You think one way, I think another way. I wanted to discuss what think other people. But looks like still nobody can understand my point, or may be I cannot express it...
By mixed up, I meant that you seemed to be valuing the int[] x version over the int[] const x version. Other than that, what I stated is an indisputable fact -- the function cannot alter x's bounds. -Steve
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 The D principle - "The program compile and runs as expected, 
 or not compile at all".
This is a fantasy. The compiler cannot know what you expect.
The language is needed to express your intentions to the compiler.
Anything that the compiler cannot enforce is just documentation. Does it matter whether the documentation is in a comment or part of the signature?
Why we need "const" keyword, while we can just put "I promise do not change it" in the documentation?
 The language should be expressive as possible. My point is to 
 push programmers to write correct software. I just ask these 
 forum to think about topic. If everybody satisfied by void 
 fun(int[] x){} behavior, then I just go...
I'm not trying to be a bully or anything, but you are not understanding that what you want is a guarantee of logic, that the compiler cannot possibly enforce.
I am telling to the compiler what to do, or what not to do, with help of programming language. I am controlling the compilation result with the language. I am not waiting that compiler will anticipate my thoughts. But if I write "const x" the variable must be const. If i have language construct that can give me stronger guarantees, i will use it.
 Your example (setting a variable to another value) being 
 rejected is easy to work around, just use another variable that 
 doesn't have that restriction.
My goal is not to implement some functionality, and I don't know how to do it. I want to make programmer's intentions more clear with a help of language. What you are saying is approximately - "If everybody will tune the language to his private needs, it will be not good". I agree with such point. Therefore I asked in the forum - am I alone who wants to tune this language feature? If you are experienced programmer in D your brain filter such cases automatically.
 There are so many examples of behavior-enforcing features that 
 result in something that the creator of the feature didn't want 
 or expect. Keep in mind there are years of projects that have 
 been written assuming that you can use a standard slice in the 
 signature of a function, we don't want to invalidate all that 
 code just because programmers sometimes write buggy code.
I don't even thought to invalidate already written code.
 But I encounter a bug in my program that was due to my 
 misusing of such signature.
Here's where the gray area is. If you make this error once or twice, but never again, is it worth changing the language over? Probably not. But if you make this error every day, even KNOWING what will happen, it's worth looking into. But any changes have to avoid a negative impact on existing code as much as possible. Think of the person who knows how slices work, who very seldom makes this mistake, and who now has to go through all his code and make this change, never finding an instance where it makes a difference. At this point, I'm not sure what you are proposing, but I don't think there is much value in changing the way slice passing works.
 If you really need to call function that can change content 
 of an array, but cannot change size of an array the language 
 syntax should allow express it in function signature. I 
 consider "void fun(int[] const x){}" more error prone than 
 "void fun(int[] x){}" and for the caller and for implemeter.
Not sure if something is mixed up there. I think void fun(int[] x) is sufficient to describe what you say. The function cannot alter x's array bounds at all, and can alter it's data.
Here is the reason why i post the topic on this forum. You think one way, I think another way. I wanted to discuss what think other people. But looks like still nobody can understand my point, or may be I cannot express it...
By mixed up, I meant that you seemed to be valuing the int[] x version over the int[] const x version. Other than that, what I stated is an indisputable fact -- the function cannot alter x's bounds. -Steve
Jan 30 2014
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 13:58:55 -0500, Cooler <kulkin hotbox.ru> wrote:

 The D principle - "The program compile and runs as expected, or not  
 compile at all".
This is a fantasy. The compiler cannot know what you expect.
The language is needed to express your intentions to the compiler.
Anything that the compiler cannot enforce is just documentation. Does it matter whether the documentation is in a comment or part of the signature?
Why we need "const" keyword, while we can just put "I promise do not change it" in the documentation?
const is only useful in cases where references are involved. In this case, you want to make the *copied* data constant, in hopes that you then can't accidentally stop referencing the original. In another example, there is little to be gained from a signature such as: void foo(const int x); What does this say about foo? Nothing. I can pass in a mutable, const, or immutable int, because a copy is made. It doesn't provide any more guarantees to the caller than: void foo(int x); Likewise, your proposed "int[] const" would not guarantee anything extra to the caller beyond "int[]", because both are copies put onto the stack. The function has no access to the original values. Arrays in D are hard to understand. They don't behave like arrays in most other languages. But they foster a different mindset I think, that results in some of the fastest code on the planet. But one has to understand the semantics of syntax if they want to properly use the language. For functions which append/extend and then write data to the prior piece (the only non-deterministic case), special care has to be taken to explain this to the caller. I would think a mechanism to attempt detecting this and flagging it would be a worthy lint tool feature. But not a language or compiler feature. There is just simply no way to say "that is always bad" or that you know what the intentions of the author are. The other case you specified, when the author re-assigns a slice and expects it to be a memcpy or to re-bind the calling parameter, the result is deterministically the wrong result, and the coder will notice it right away (and hopefully correct their understanding). I don't think we need a language feature for that, just documentation (which I think we have). Let me also suggest you use scope statements to verify at runtime that the case you intend to prevent doesn't actually happen: void foo(int[] x) { const origx = x; scope(exit) assert(origx.ptr == x.ptr); ... } While not perfect, and not static, it should at least avoid subtle bugs (and can be turned off in release mode). -Steve
Jan 30 2014
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 30 January 2014 at 16:48:51 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 16:18:33 UTC, Steven 
 Schveighoffer wrote:
 void foo(int x)
 {
   x = 5;
 }

 "hey, why doesn't that work! Setting a parameter to another 
 value should be illegal!"

 -Steve
Please understand - I am not against void foo(int[] x){} I am for predictability of behavior.
Predictability of behavior is not a principle of D and even if it would be, it can't be applied blindly. D is not a formal mathematic system.
 You suggest to describe function's behavior in documentation - 
 quotation from your article "It is a good idea to note in the 
 documentation how the passed in slice might or might not be
 overwritten." My idea is that all potential errors must be 
 detected as soon as possible.
It is impossible to detect all errors in D per se, let alone taking into account separate compilation model. In some circumstances compiler can guess possible ways, but particular case we discussing is so common, that nothing can be done to 'fix' it. By the way, this case is not strictly speaking an error. It is error in context when caller cares about changes but this can be hardly verified at compile time (compiler need to read brain to know it).
 The D principle - "The program compile and runs as expected, or 
 not compile at all".
It is all talk. Trying to apply this 'principle' in all cases is too naive.
 If you really need to call function that can change content of 
 an array, but cannot change size of an array the language 
 syntax should allow express it in function signature. I 
 consider "void fun(int[] const x){}" more error prone than 
 "void fun(int[] x){}" and for the caller and for implemeter.
Personally this syntax is awful. By the way, there is another similar issue in D: import std.stdio; void foo(int[int] aa) { aa[1] = 1; } void main() { int[int] aa; foo(aa); writeln(aa); // [] aa[0] = 0; foo(aa); writeln(aa); // [0:0, 1:1] aa = null; foo(aa); writeln(aa); // [] } Here changes in AA array will be visible conditional that array is non null. If it is null, changes will be lost. This is another example of situation of "the caller MAY or MAY NOT see changes to" (citing your post above). In general, such semivalue-semireference semantic is produced when there is pointer wrapped into struct (doesn't matter whether it is opaque lang type or user defined). This happens in some language types, but may be also in user defined types. I don't think that verifying arbitrary semantic is compiler job.
Jan 30 2014
prev sibling parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 16:18:33 UTC, Steven 
Schveighoffer wrote:
 On Thu, 30 Jan 2014 11:04:44 -0500, Cooler <kulkin hotbox.ru> 
 wrote:

 On Thursday, 30 January 2014 at 16:01:32 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:59:48 UTC, Cooler wrote:
 On Thursday, 30 January 2014 at 15:51:44 UTC, Tobias 
 Pankrath wrote:
 On Thursday, 30 January 2014 at 15:49:35 UTC, Cooler wrote:
 I agree. I just want that the case can be expressed in 
 language syntax more obvious - something like "fun(int[] 
 const x){}" to emphasize that I understand that fun() 
 can change content of array, and cannot change the 
 {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
It's [0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
No!!! It depends how runtime allocates memory for the array. Read http://dlang.org/d-array-article.html. If 'a' has internal space enough to place '5' the caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].
Sorry!!! My mistake. The caller will see [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] :)
But that is the proof. The intention of fun() implementer will not be achieved. And such misunderstanding will appear only at runtime.
void foo(int x) { x = 5; } "hey, why doesn't that work! Setting a parameter to another value should be illegal!" -Steve
Difference is here. "void foo(int x){}" - the caller will NEVER see any change to 'x'. "void foo(int[] x){}" - the caller MAY or MAY NOT see changes to 'x'.
Jan 30 2014
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 12:07:07 -0500, Cooler <kulkin hotbox.ru> wrote:

 On Thursday, 30 January 2014 at 16:18:33 UTC, Steven Schveighoffer wrote:
 void foo(int x)
 {
    x = 5;
 }

 "hey, why doesn't that work! Setting a parameter to another value  
 should be illegal!"
Difference is here. "void foo(int x){}" - the caller will NEVER see any change to 'x'. "void foo(int[] x){}" - the caller MAY or MAY NOT see changes to 'x'.
This is incorrect: foo(int[] x){} - The caller will see changes to data 'x' references. A slice is a reference type, it references a specific block of data. It's more akin to a pointer than an int. I could change my example: void foo(int *x) { int n = 3; x = &n; } "hey, why doesn't x now point to 3? Should be illegal!" -Steve
Jan 30 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 10:49:34 -0500, Cooler <kulkin hotbox.ru> wrote:

 On Thursday, 30 January 2014 at 15:29:50 UTC, Steven Schveighoffer wrote:
 On Thu, 30 Jan 2014 10:24:14 -0500, Cooler <kulkin hotbox.ru> wrote:

 On Thursday, 30 January 2014 at 14:40:36 UTC, Dicebot wrote:
 I agree. I just want that the case can be expressed in language syntax  
 more obvious - something like "fun(int[] const x){}" to emphasize that  
 I understand that fun() can change content of array, and cannot change  
 the {pointer,size} pair.
That's what fun(int[] x) does :) -Steve
Again... void fun(int[] x){ x ~= 5; } auto a = new int[10]; fun(a); // Can you predict the content of 'a'?
I suspect you mean: void fun(int[] x) {x.length += 1; x[0] = 5;} I cannot predict what the caller will see. But this is not a problem of *signatures*. The caller will not see ANY changes to {pointer,size} pair of x. That is the point -- it's passed by value. Note that this implementation is completely predictable: void fun(int[] x) {x[0] = 5; x.length += 1;} I want to stress that just because you can find an implementation that has a bug doesn't mean that there is an opportunity for the compiler to detect that bug, especially a logic bug. The compiler simply cannot know what you are thinking.
 In your case:
 void fun(int[] x){ x = [1, 2]; } // Compilation ok. Implementation's  
 error.
 The fun() implementer made error and think that caller will get new  
 array. But it will get it only at runtime!
 If we for example (just for example) have
 void fun(int[] const x){ x = [1, 2]; } // Compilation error.
I see very little value in that. We don't need to obliterate a tremendous amount of slice usage (not mentioning how much code will have to be updated) in order to help newbies understand how slices work. -Steve
Jan 30 2014
prev sibling parent reply "Sergei Nosov" <sergei.nosov gmail.com> writes:
On Wednesday, 29 January 2014 at 13:15:30 UTC, Cooler wrote:
 On Wednesday, 29 January 2014 at 12:40:00 UTC, Tobias Pankrath 
 wrote:
 On Wednesday, 29 January 2014 at 11:46:23 UTC, Cooler wrote:
 Thank you for detailed explanation. But the question is - "Is 
 that correct that language allows ambiguous behavior?"
Where is it ambiguous?
Ambiguity is here... When I call fun1() or fun2() I know the behavior directly from function signature (read the comments in my first post). For fun3() case the caller side don't know the behavior directly from function signature. To know what will happen with array "a", the caller must see to fun3() body. Ambiguity is - in first and second cases the caller knows what happens with "a", but in third case the caller does not know what happens with "a".
I believe you encounter an array reallocation. If fun3 doesn't change the size of the array - you will see every change made by fun3 to the contents of `a` (but the `a` itself cannot be changed - only the contents). No other way around. If, however, in fun3 you change the size of the array - it may reallocate. Like, if you're appending to `x` - it will allocate a new array and make x point to it. Now `a` and `x` point to distinct arrays. And any change you do using `x` won't be seen by `a`. And, yes, this is the intended behavior.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 If, however, in fun3 you change the size of the array - it may 
 reallocate. Like, if you're appending to `x` - it will allocate 
 a new array and make x point to it. Now `a` and `x` point to 
 distinct arrays. And any change you do using `x` won't be seen 
 by `a`. And, yes, this is the intended behavior.
That exactly what am i asking for! You have to know the body of fun3() to predict what happened with 'a'. When I call fun() there are 3 intentions: 1. I want just send the contents of 'a' to fun(). It can be done by a.dup, or you must sure that fun() has 'in' qualifier for it's argument. 2. I want fun() change content of 'a' array. This can be done by ensuring fun() has 'ref' qualifier for it's argument. What intention should I have to call fun3()?
Jan 29 2014
parent "Sergei Nosov" <sergei.nosov gmail.com> writes:
On Wednesday, 29 January 2014 at 14:48:12 UTC, Cooler wrote:
 If, however, in fun3 you change the size of the array - it may 
 reallocate. Like, if you're appending to `x` - it will 
 allocate a new array and make x point to it. Now `a` and `x` 
 point to distinct arrays. And any change you do using `x` 
 won't be seen by `a`. And, yes, this is the intended behavior.
That exactly what am i asking for! You have to know the body of fun3() to predict what happened with 'a'. When I call fun() there are 3 intentions: 1. I want just send the contents of 'a' to fun(). It can be done by a.dup, or you must sure that fun() has 'in' qualifier for it's argument. 2. I want fun() change content of 'a' array. This can be done by ensuring fun() has 'ref' qualifier for it's argument. What intention should I have to call fun3()?
E.g. you want fun3 to change the content of the array, but you don't want it to change the pointer. Consider, void fun2(ref int[] x) { x = null; } void fun3(int[] x) { x = null; } void main() { int[] a = [1, 2, 3]; fun3(a); assert(a); fun2(a); assert(!a); }
Jan 29 2014
prev sibling next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/29/2014 02:55 AM, Cooler wrote:
 Consider 3 functions taking array as an argument:

 void fun1(in  int[] x){...}
 void fun2(ref int[] x){...}
 void fun3(    int[] x){...}

 auto a = new int[10];

 fun1(a); // Guaranteed that "a" will not be changed
 fun2(a); // Guaranteed that we will see any change to "a", made in fun2()
 fun3(a); // Changes to "a" in fun3() may be or may be not visible to the
 caller

 In case of fun3() we have ambiguous behaviour, depending on the body of
 the function.

 Am I right?
 Is that intentional?
Yes, that is how slices work in D. The following article explains the "non-determinism" that you mention: http://dlang.org/d-array-article.html Ali
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 Yes, that is how slices work in D. The following article 
 explains the "non-determinism" that you mention:

   http://dlang.org/d-array-article.html

 Ali
Thank you for the article. Quotation from the article "It is a good idea to note in the documentation how the passed in slice might or might not be overwritten." May be just prohibit at language level the case of fun3() function, to do not allow unpredictable behavior?
Jan 29 2014
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 15:11:33 UTC, Cooler wrote:
 Yes, that is how slices work in D. The following article 
 explains the "non-determinism" that you mention:

  http://dlang.org/d-array-article.html

 Ali
Thank you for the article. Quotation from the article "It is a good idea to note in the documentation how the passed in slice might or might not be overwritten." May be just prohibit at language level the case of fun3() function, to do not allow unpredictable behavior?
It's not unpredictable, at least not more unpredictable then fun2. Should we dissallow this two? int[] a = [1,2,3,4]; b = a; b ~= [5, 6, 7, 8];
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 It's not unpredictable, at least not more unpredictable then 
 fun2.

 Should we dissallow this two?

 int[] a = [1,2,3,4];
 b = a;
 b ~= [5, 6, 7, 8];
You don't understand me. You consider that I am author of the fun() and the caller side. Read two post above http://forum.dlang.org/post/dxqxlhyhmdfuashhmtrz forum.dlang.org I consider that author of fun() and author of caller side are different persons. They must agree between them how to provide some functionality. If I fun()'s author why do I need to provide fun3() if I already provided fun1() and fun2()? I I caller's author - why may I require fun3() variant, if I already have fun1() and fun2()?
Jan 29 2014
parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 15:38:34 UTC, Cooler wrote:
 It's not unpredictable, at least not more unpredictable then 
 fun2.

 Should we dissallow this two?

 int[] a = [1,2,3,4];
 b = a;
 b ~= [5, 6, 7, 8];
You don't understand me. You consider that I am author of the fun() and the caller side. Read two post above http://forum.dlang.org/post/dxqxlhyhmdfuashhmtrz forum.dlang.org I consider that author of fun() and author of caller side are different persons. They must agree between them how to provide some functionality. If I fun()'s author why do I need to provide fun3() if I already provided fun1() and fun2()? I I caller's author - why may I require fun3() variant, if I already have fun1() and fun2()?
Oh, I guess I understood quite well. I just don't see a special problem with arrays, it's just the same as any other aliasing issue. Take this for example: void funS(S* s); // S is a struct, maybe containing a ptr and a length member :-) Should we disallow this as well? When I give a unqualified pointer to a function, the only guarantee I get is that it's pointing to the same location after the call. It's the same with arrays. And since we have this problem every time we pass a pointer to function I don't see why arrays should get special treatment.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Wednesday, 29 January 2014 at 15:56:50 UTC, Tobias Pankrath 
wrote:
 On Wednesday, 29 January 2014 at 15:38:34 UTC, Cooler wrote:
 It's not unpredictable, at least not more unpredictable then 
 fun2.

 Should we dissallow this two?

 int[] a = [1,2,3,4];
 b = a;
 b ~= [5, 6, 7, 8];
You don't understand me. You consider that I am author of the fun() and the caller side. Read two post above http://forum.dlang.org/post/dxqxlhyhmdfuashhmtrz forum.dlang.org I consider that author of fun() and author of caller side are different persons. They must agree between them how to provide some functionality. If I fun()'s author why do I need to provide fun3() if I already provided fun1() and fun2()? I I caller's author - why may I require fun3() variant, if I already have fun1() and fun2()?
Oh, I guess I understood quite well. I just don't see a special problem with arrays, it's just the same as any other aliasing issue. Take this for example: void funS(S* s); // S is a struct, maybe containing a ptr and a length member :-) Should we disallow this as well? When I give a unqualified pointer to a function, the only guarantee I get is that it's pointing to the same location after the call. It's the same with arrays. And since we have this problem every time we pass a pointer to function I don't see why arrays should get special treatment.
Do you read my post? I am answering... why do I need fun3() if I already have fun1() and fun2().
Jan 29 2014
parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 16:01:08 UTC, Cooler wrote:
 Do you read my post? I am answering... why do I need fun3() if 
 I already have fun1() and fun2().
fun3 guarantees that the argument has the same length for example.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Wednesday, 29 January 2014 at 16:15:36 UTC, Tobias Pankrath 
wrote:
 On Wednesday, 29 January 2014 at 16:01:08 UTC, Cooler wrote:
 Do you read my post? I am answering... why do I need fun3() if 
 I already have fun1() and fun2().
fun3 guarantees that the argument has the same length for example.
Where argument has the same length? After function call, or inside function? I don't understand what my intention should be to push me to use fun3()?
Jan 29 2014
next sibling parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Wednesday, 29 January 2014 at 16:26:05 UTC, Cooler wrote:

 Where argument has the same length? After function call, or 
 inside function? I don't understand what my intention should be 
 to push me to use fun3()?
Gosh. To allow the function to modify the contents, but not the size of the array.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Wednesday, 29 January 2014 at 16:36:44 UTC, Stanislav Blinov
wrote:
 On Wednesday, 29 January 2014 at 16:26:05 UTC, Cooler wrote:

 Where argument has the same length? After function call, or 
 inside function? I don't understand what my intention should 
 be to push me to use fun3()?
Gosh. To allow the function to modify the contents, but not the size of the array.
Сам то понял чего написал??? :)
Jan 29 2014
parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Wednesday, 29 January 2014 at 16:54:27 UTC, Cooler wrote:
 On Wednesday, 29 January 2014 at 16:36:44 UTC, Stanislav Blinov
 wrote:
 On Wednesday, 29 January 2014 at 16:26:05 UTC, Cooler wrote:

 Where argument has the same length? After function call, or 
 inside function? I don't understand what my intention should 
 be to push me to use fun3()?
Gosh. To allow the function to modify the contents, but not the size of the array.
Сам то понял чего написал??? :)
Yes I did.
Jan 29 2014
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Wednesday, 29 January 2014 at 16:26:05 UTC, Cooler wrote:
 Where argument has the same length? After function call, or 
 inside function? I don't understand what my intention should be 
 to push me to use fun3()?
Where argument has the same length? After function call, or inside function? I don't understand what my intention should be to push me to use fun3()? int[] a = [1,2,3]; // length == 3 fun3(a); assert(a.length == 3); // holds. If you have fun1 and fun2 you might have no use case for fun3. That is however no reason to disallow fun3, especially since there are situations where you don't won't to have fun2. Keep in mind, that I'm only arguing against a ban of fun3 and the notion that it's behaviour is more unpredictable then fun2 although it offers stronger guarantees.
Jan 29 2014
prev sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 29 January 2014 at 16:26:05 UTC, Cooler wrote:
 On Wednesday, 29 January 2014 at 16:15:36 UTC, Tobias Pankrath 
 wrote:
 On Wednesday, 29 January 2014 at 16:01:08 UTC, Cooler wrote:
 Do you read my post? I am answering... why do I need fun3() 
 if I already have fun1() and fun2().
fun3 guarantees that the argument has the same length for example.
Where argument has the same length? After function call, or inside function? I don't understand what my intention should be to push me to use fun3()?
If you want to modify the slice and make changes visible in caller, you should use ref. If you don't care whether changes are visible in caller, you can omit any attributes and use plain array. This belongs to the case you are asking about. If you don't want to change array in callee, pass it as const qualified. Now, after rethinking the issue I am inclining that "don't care whether changes are visible for caller" is not very wrong, but not very good design. Ideally it should be specified to avoid unexpected problems to pop up. So yes, it is better to qualify array. Another point. This casino games of whether changes would be visible or not is direct consequence of how slices are implemented (and how runtime service them). Remember, that many features in D work in a strange way not because of wise design but as a consequence of not fully thought design (like array). As a result, some features work in not best way they should. Although many folks in newsgroups would eagerly say that you don't understand the lang, it wouldn't make a bad design a good one.
Jan 29 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 If you want to modify the slice and make changes visible in 
 caller, you should use ref. If you don't care whether changes 
 are visible in caller, you can omit any attributes and use 
 plain array. This belongs to the case you are asking about. If 
 you don't want to change array in callee, pass it as const 
 qualified.

 Now, after rethinking the issue I am inclining that "don't care 
 whether changes are visible for caller" is not very wrong, but 
 not very good design. Ideally it should be specified to avoid 
 unexpected problems to pop up. So yes, it is better to qualify 
 array.

 Another point. This casino games of whether changes would be 
 visible or not is direct consequence of how slices are 
 implemented (and how runtime service them). Remember, that many 
 features in D work in a strange way not because of wise design 
 but as a consequence of not fully thought design (like array). 
 As a result, some features work in not best way they should. 
 Although many folks in newsgroups would eagerly say that you 
 don't understand the lang, it wouldn't make a bad design a good 
 one.
Please stop explain me how fun3() works. I know that. One of the main idea of D is that things must work as planned, or would not compile at all. First and second variants follow this idea. But fun3() can work not as planned on the caller side (depends on fun3() body's implementation). The question again - may be prohibit fun3() variant? If we prohibit it, what use cases we could not implement with fun1() and fun2()?
Jan 30 2014
next sibling parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Thursday, 30 January 2014 at 09:14:43 UTC, Cooler wrote:

 Please stop explain me how fun3() works. I know that.
 One of the main idea of D is that things must work as planned, 
 or would not compile at all. First and second variants follow 
 this idea. But fun3() can work not as planned on the caller 
 side (depends on fun3() body's implementation).
 The question again - may be prohibit fun3() variant? If we 
 prohibit it, what use cases we could not implement with fun1() 
 and fun2()?
Goodness... You've been shown this use case like a zillion times already: the caller manages her buffer, fun3() is allowed to change contents, but not reallocate caller's buffer. fun1() can't provide that (const), fun2() cannot either, because it explicitly allows reallocation (ref). This behavior is only provided by fun3(). So it's either that, or indeed cases of "I don't care about this array", which Maxim Fomin has mentioned.
Jan 30 2014
parent "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 09:26:56 UTC, Stanislav Blinov 
wrote:
 On Thursday, 30 January 2014 at 09:14:43 UTC, Cooler wrote:

 Please stop explain me how fun3() works. I know that.
 One of the main idea of D is that things must work as planned, 
 or would not compile at all. First and second variants follow 
 this idea. But fun3() can work not as planned on the caller 
 side (depends on fun3() body's implementation).
 The question again - may be prohibit fun3() variant? If we 
 prohibit it, what use cases we could not implement with fun1() 
 and fun2()?
Goodness... You've been shown this use case like a zillion times already: the caller manages her buffer, fun3() is allowed to change contents, but not reallocate caller's buffer. fun1() can't provide that (const), fun2() cannot either, because it explicitly allows reallocation (ref). This behavior is only provided by fun3(). So it's either that, or indeed cases of "I don't care about this array", which Maxim Fomin has mentioned.
May be I am dumb... If "I don't care about this array", I can use any of fun1() or fun2(). Can you formulate contract to fun3()? Something like "Change content of an array, but if you need to change the size - don't do it!" Do you think so?
Jan 30 2014
prev sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 30 January 2014 at 09:14:43 UTC, Cooler wrote:
 If you want to modify the slice and make changes visible in 
 caller, you should use ref. If you don't care whether changes 
 are visible in caller, you can omit any attributes and use 
 plain array. This belongs to the case you are asking about. If 
 you don't want to change array in callee, pass it as const 
 qualified.

 Now, after rethinking the issue I am inclining that "don't 
 care whether changes are visible for caller" is not very 
 wrong, but not very good design. Ideally it should be 
 specified to avoid unexpected problems to pop up. So yes, it 
 is better to qualify array.

 Another point. This casino games of whether changes would be 
 visible or not is direct consequence of how slices are 
 implemented (and how runtime service them). Remember, that 
 many features in D work in a strange way not because of wise 
 design but as a consequence of not fully thought design (like 
 array). As a result, some features work in not best way they 
 should. Although many folks in newsgroups would eagerly say 
 that you don't understand the lang, it wouldn't make a bad 
 design a good one.
Please stop explain me how fun3() works. I know that.
This is first problem. You are being explained what is the *purpose* of the fun3() but you repeatedly fail to accept it.
 One of the main idea of D is that things must work as planned, 
 or would not compile at all.
Outcryingly wrong. Study bugzilla which shows how some things go wrong and read DIPs to learn that there are some issues in the language for which the communitty still struggles to formulate good solution.
 First and second variants follow this idea. But fun3() can work 
 not as planned on the caller side (depends on fun3() body's 
 implementation).
Many things can work not as intended. Please read forums, bugzilla, etc. I bet passing array will not be the only thing you find confusing.
 The question again - may be prohibit fun3() variant?
Prohibiting code like: void foo(int[] arr) {} would break hell of a code and pose doubts on what happens with arrays if so simple construction is prohibited. In addition, I mentioned that "don't care" is probably sometimes an option. Emitting warning here has some merits but it would be consitently ignored (I expect).
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
 Please stop explain me how fun3() works. I know that.
This is first problem. You are being explained what is the *purpose* of the fun3() but you repeatedly fail to accept it.
No odds.
 One of the main idea of D is that things must work as planned, 
 or would not compile at all.
Outcryingly wrong. Study bugzilla which shows how some things go wrong and read DIPs to learn that there are some issues in the language for which the communitty still struggles to formulate good solution.
Now I am trying to speak ideally. What ideal language should be, not the practical implementation.
 First and second variants follow this idea. But fun3() can 
 work not as planned on the caller side (depends on fun3() 
 body's implementation).
Many things can work not as intended. Please read forums, bugzilla, etc. I bet passing array will not be the only thing you find confusing.
Again - don't look back. Consider how we can make D better.
 The question again - may be prohibit fun3() variant?
Prohibiting code like: void foo(int[] arr) {} would break hell of a code and pose doubts on what happens with arrays if so simple construction is prohibited. In addition, I mentioned that "don't care" is probably sometimes an option. Emitting warning here has some merits but it would be consitently ignored (I expect).
Again - stop consider current state of D implementation. Consider how we can make D better. I think fun3() push programmers to make errors.
Jan 30 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Cooler:

 Again - stop consider current state of D implementation. 
 Consider how we can make D better. I think fun3() push 
 programmers to make errors.
I think functions like void fun(int[] a){} are bug prone, because you seem to change the length of the array inside the function, or if you perform an append you modify a new memory zone, but such changes are invisible from the caller. Some times this is what you want, and in some cases this is a programmer mistake (this bug happened to me several times). But I don't know what to change and if this situation can be improved now. Perhaps all that's left to improve is to add tests to a D lint that warns against this possible source of bugs. Bye, bearophile
Jan 30 2014
parent "Cooler" <kulkin hotbox.ru> writes:
 Again - stop consider current state of D implementation. 
 Consider how we can make D better. I think fun3() push 
 programmers to make errors.
I think functions like void fun(int[] a){} are bug prone, because you seem to change the length of the array inside the function, or if you perform an append you modify a new memory zone, but such changes are invisible from the caller. Some times this is what you want, and in some cases this is a programmer mistake (this bug happened to me several times). But I don't know what to change and if this situation can be improved now. Perhaps all that's left to improve is to add tests to a D lint that warns against this possible source of bugs. Bye, bearophile
You wrote "this bug happened to me several times" :) This bug also happened with me :) I know that everybody uses variant of fun(int[] a){} everywhere. I understand that nobody will rewrite it source code. I just want to follow rule "Correct things must be simple. Wrong things must be difficult." As an example we can mark such usage as "deprecated", to push all programmers use more error prone variants.
Jan 30 2014
prev sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 30 January 2014 at 10:49:42 UTC, Cooler wrote:
 Now I am trying to speak ideally. What ideal language should 
 be, not the practical implementation.
...
 Again - don't look back. Consider how we can make D better.
...
 Again - stop consider current state of D implementation. 
 Consider how we can make D better. I think fun3() push 
 programmers to make errors.
Looks like you are overestimating yourself. Now, try: 1) write pull request to reject the code 2) convince developers somehow that your proposal is good 3) convince Walter to accept the change 4) after merging pull convince angry maintainers that it is good idea to reject unqualified array passing because Cooler tries to make the language better
Jan 30 2014
parent "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 11:06:03 UTC, Maxim Fomin wrote:
 On Thursday, 30 January 2014 at 10:49:42 UTC, Cooler wrote:
 Now I am trying to speak ideally. What ideal language should 
 be, not the practical implementation.
...
 Again - don't look back. Consider how we can make D better.
...
 Again - stop consider current state of D implementation. 
 Consider how we can make D better. I think fun3() push 
 programmers to make errors.
Looks like you are overestimating yourself. Now, try: 1) write pull request to reject the code 2) convince developers somehow that your proposal is good 3) convince Walter to accept the change 4) after merging pull convince angry maintainers that it is good idea to reject unqualified array passing because Cooler tries to make the language better
Agree. As a first step I post this topic in the forum. If I found understanding I will follow all steps you mention :)
Jan 30 2014
prev sibling parent reply "Sergei Nosov" <sergei.nosov gmail.com> writes:
On Wednesday, 29 January 2014 at 15:11:33 UTC, Cooler wrote:
 Yes, that is how slices work in D. The following article 
 explains the "non-determinism" that you mention:

  http://dlang.org/d-array-article.html

 Ali
Thank you for the article. Quotation from the article "It is a good idea to note in the documentation how the passed in slice might or might not be overwritten." May be just prohibit at language level the case of fun3() function, to do not allow unpredictable behavior?
This behavior is just a consequence of the deliberate decision on how arrays should be implemented. Any decision would be a trade-off. Like, if you just disallow this signature, you will have to use .dup at the caller side if you want the semantics of fun3. And often this copy might be unnecessary. It's really like a ball under the carpet. You make it flat in one place, but the ball pops up in the other. The trade-off that D chooses is pretty reasonable. You just have to accept that and get used to it.
Jan 29 2014
parent "Cooler" <kulkin hotbox.ru> writes:
 This behavior is just a consequence of the deliberate decision 
 on how arrays should be implemented.

 Any decision would be a trade-off. Like, if you just disallow 
 this signature, you will have to use .dup at the caller side if 
 you want the semantics of fun3. And often this copy might be 
 unnecessary.

 It's really like a ball under the carpet. You make it flat in 
 one place, but the ball pops up in the other.

 The trade-off that D chooses is pretty reasonable. You just 
 have to accept that and get used to it.
Ok! I agree with you! Can you just answer me http://forum.dlang.org/post/dxqxlhyhmdfuashhmtrz forum.dlang.org
Jan 29 2014
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
kOn Wed, 29 Jan 2014 05:55:56 -0500, Cooler <kulkin hotbox.ru> wrote:

 Consider 3 functions taking array as an argument:

 void fun1(in  int[] x){...}
 void fun2(ref int[] x){...}
 void fun3(    int[] x){...}

 auto a = new int[10];

 fun1(a); // Guaranteed that "a" will not be changed
 fun2(a); // Guaranteed that we will see any change to "a", made in fun2()
 fun3(a); // Changes to "a" in fun3() may be or may be not visible to the  
 caller

 In case of fun3() we have ambiguous behaviour, depending on the body of  
 the function.

 Am I right?
Yes.
 Is that intentional?
Yes. I read the rest of the discussion. Arrays are hard to understand in D, especially if you have preconceived notions from other languages. But I would point out that fun2 does not "guarantee" anything more than fun3: void fun2(ref int [] x) { fun3(x); } It is an intrinsic property of slices that they do not own the data pointed at. You cannot guarantee that one slice's changes will affect another slice, AS LONG AS one slice is increasing its length. If you avoid increasing the length, then the results are deterministic. As I said in the article, avoid increasing the length, and THEN changing the original data. If you do that, you should have quite predictable results. If your code must do otherwise, explain in the documentation what should happen (i.e. don't use the passed-in slice after the function), and use some of the tricks to avoid it (use ref, or return the new slice data). -Steve
Jan 30 2014
parent reply "Cooler" <kulkin hotbox.ru> writes:
On Thursday, 30 January 2014 at 13:44:39 UTC, Steven 
Schveighoffer wrote:
 kOn Wed, 29 Jan 2014 05:55:56 -0500, Cooler <kulkin hotbox.ru> 
 wrote:

 Consider 3 functions taking array as an argument:

 void fun1(in  int[] x){...}
 void fun2(ref int[] x){...}
 void fun3(    int[] x){...}

 auto a = new int[10];

 fun1(a); // Guaranteed that "a" will not be changed
 fun2(a); // Guaranteed that we will see any change to "a", 
 made in fun2()
 fun3(a); // Changes to "a" in fun3() may be or may be not 
 visible to the caller

 In case of fun3() we have ambiguous behaviour, depending on 
 the body of the function.

 Am I right?
Yes.
 Is that intentional?
Yes. I read the rest of the discussion. Arrays are hard to understand in D, especially if you have preconceived notions from other languages. But I would point out that fun2 does not "guarantee" anything more than fun3: void fun2(ref int [] x) { fun3(x); }
If I don't want that fun() will change my array, i have to use fun1() variant. If I want fun() will change my array, i have to use fun2() variant. What fun2() do with it's argument inside it's body - not my business.
 It is an intrinsic property of slices that they do not own the 
 data pointed at. You cannot guarantee that one slice's changes 
 will affect another slice, AS LONG AS one slice is increasing 
 its length. If you avoid increasing the length, then the 
 results are deterministic. As I said in the article, avoid 
 increasing the length, and THEN changing the original data. If 
 you do that, you should have quite predictable results. If your 
 code must do otherwise, explain in the documentation what 
 should happen (i.e. don't use the passed-in slice after the 
 function), and use some of the tricks to avoid it (use ref, or 
 return the new slice data).

 -Steve
1. For example somebody already implemented fun1() and fun2() variants, as in my first post. This variants understandable and predictable. What can push me to ask another person for fun3() implementation, while it result is unpredictable, until you know fun3() body? 2. You wrote "If your code must do otherwise, explain in the documentation what should happen". That exactly what I am trying to discuss here. Instead of writing some documentation, just warn (and may be prohibit in far-far future) about such possible unpredictability during compilation.
Jan 30 2014
next sibling parent reply "Cooler" <kulkin hotbox.ru> writes:
Forgot to mention :)

 I read the rest of the discussion. Arrays are hard to 
 understand in D, especially if you have preconceived notions 
 from other languages. But I would point out that fun2 does not 
 "guarantee" anything more than fun3:

 void fun2(ref int [] x)
 {
   fun3(x);
 }
"But I would point out that fun2 does not guarantee anything more than fun3:" - fun2() cannot guarantee anything because it calls fun3() which in turn cannot guarantee anything.
Jan 30 2014
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Thursday, 30 January 2014 at 14:18:41 UTC, Cooler wrote:
 "But I would point out that fun2 does not guarantee anything 
 more than fun3:" - fun2() cannot guarantee anything because it 
 calls fun3() which in turn cannot guarantee anything.
Unrelated. void foo2(ref int[] arr) { /* do nothing */ } You can't guarantee mutation by function signature. Well, unless compiler does full attribute and qualifier inference.
Jan 30 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 09:18:40 -0500, Cooler <kulkin hotbox.ru> wrote:

 Forgot to mention :)

 I read the rest of the discussion. Arrays are hard to understand in D,  
 especially if you have preconceived notions from other languages. But  
 I would point out that fun2 does not "guarantee" anything more than  
 fun3:

 void fun2(ref int [] x)
 {
   fun3(x);
 }
"But I would point out that fun2 does not guarantee anything more than fun3:" - fun2() cannot guarantee anything because it calls fun3() which in turn cannot guarantee anything.
Right, but you said this:
 fun2(a); // Guaranteed that we will see any change to "a", made in fun2()
Which is false. That was my point. -Steve
Jan 30 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jan 2014 09:07:14 -0500, Cooler <kulkin hotbox.ru> wrote:

 If I don't want that fun() will change my array, i have to use fun1()  
 variant.
 If I want fun() will change my array, i have to use fun2() variant. What  
 fun2() do with it's argument inside it's body - not my business.
No. You can use fun3 variant as well: void fun3(int[] x) { x[] = 0; // guaranteed to be seen by caller. }
 1. For example somebody already implemented fun1() and fun2() variants,  
 as in my first post. This variants understandable and predictable. What  
 can push me to ask another person for fun3() implementation, while it  
 result is unpredictable, until you know fun3() body?
The only reason to have both fun2 and fun3 variants is if you want to handle both l-value and r-value options differently. There would be very few use cases which make sense AND return void. You usually want one or the other.
 2. You wrote "If your code must do otherwise, explain in the  
 documentation what
 should happen". That exactly what I am trying to discuss here. Instead  
 of writing some documentation, just warn (and may be prohibit in far-far  
 future) about such possible unpredictability during compilation.
No, because you are not understanding the effect of the attributes, and who is responsible for what. 1. Banning such signatures does NOT accomplish what you want. 2. Such signatures do NOT guarantee what happens inside the function. Having the compiler ban the problem where you expect the caller to see your changes, but they don't, is akin to just making the compiler able to detect all logic bugs. It's not possible (NP complete). The compiler just doesn't know what you really want to do. -Steve
Jan 30 2014
prev sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 29 January 2014 at 10:55:57 UTC, Cooler wrote:
 Consider 3 functions taking array as an argument:

 void fun1(in  int[] x){...}
 void fun2(ref int[] x){...}
 void fun3(    int[] x){...}

 auto a = new int[10];

 fun1(a); // Guaranteed that "a" will not be changed
 fun2(a); // Guaranteed that we will see any change to "a", made 
 in fun2()
 fun3(a); // Changes to "a" in fun3() may be or may be not 
 visible to the caller

 In case of fun3() we have ambiguous behaviour, depending on the 
 body of the function.

 Am I right?
 Is that intentional?
I believe what you are asking for is "head const." D does not do this, search for "head const dlang" and you'll probably find some discussion in it.
Jan 30 2014