www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Round III, Brainstorm, Re: Immutable arrays for Walter and rest of us.

reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
Let's say we decided that D needs
read-only references and read-only slices
(aka const references and const slices).

Let's say that compiler can verify
constness with the same quality
as detecting that e.g. char[] type has
no property 'foobar'.

Then I would like to brainstorm about
following subjects:

1) Reasonable model of "costness"

    E.g. implicit constness (C++ style)
    const typename& - can be applied
    to any type - new const type created behind
    the scene.

    Or explicit constness. E.g. Author
    can define new array type having
    no modifying members - define new
    const type explicitly.

    Or Delphi style - const parameter is
    only for string (builtin) type.

    Or [ your choice is here ]

2) Notation. E.g. how readonly slice will look like
     in code. Function parameters, etc.

3) How to avoid "clutter". This term appears here
     not once - means that it is reasonable - so
     we need to deal with it.

-----------------------------------
Please follow rules of brainstrom in this topic:
1) No critique. Like "this is bad".
2) No applauses. Like "this is cool" .
3) Answers on answers shall contain
    only statements evolving original idea.
4) Please be as much brief as possible.

This is an experiment. I many times did brainstorm
in team but never in the news conference.
In real life BS session is limited by 16-20 minutes.
Here let's say it will be one day.

Who'll be the first?
Jul 03 2005
next sibling parent "Uwe Salomon" <post uwesalomon.de> writes:
This is too long, i know. Sorry. To sum up the contents:

* Model: Needs const variables and return types, not only const parameters.
* Notation: BasicType ConstModifier RefModifier, for example "char  
fixed[]".
* Clutter: You cannot avoid it with these requirements.




1) Reasonable model of "constness"

Most of you seem to want a mechanism that makes it possible for an object  
(read as: "some logical part of the program") to return some piece of  
information as a read-only reference. Examples:

* Slice of an array that must not be modified.
* Reference to a container of objects that only allows traversals and  
reading.


As the receiver of this information has to "prove" to the compiler that he  
will not modify the read-only reference, there must be a mechanism to  
declare and check that, too. In more complex programs the receiver often  
does not work on the information himself, but passes them to some other  
part of the program ─ which must in turn ensure that he will not modify it.


Any implementation of constness that does not provide the above points is  
not useful as a help for designing & debugging of a program. Thus it will  
not satisfy those of you who want exactly that, those who want something  
like C++ const. It may be useful for program optimization (speed), but  
that is rarely requested apparently.



2) Notation.

As we need not only const function parameters, but also const return types  
and const variables (see above), there should be a syntax like this:

BasicType ConstModifier RefModifier

BasicType is int, char, ...
ConstModifier could be "immutable", "#", "fixed", ...
RefModifier is *, []


Examples:

char#[] is an array whose contents must not be changed. BUT the array  
length etc. may be changed.
int fixed* is a pointer to an integer that must not be changed. BUT the  
pointer address may be changed.



I do not know how to make this syntax work with object references (as  
there is no RefModifier there, they are implicitly references).



3) Clutter.

In a well-designed program each part should be as independent from the  
others as possible. This includes that he implements not only the  
operations that are needed by the rest of the program, but the "full range  
of reasonable operations".


This makes it necessary for every container to provide a non-const API  
(containers whose contents cannot be changed are useless) and a const API  
(otherwise it cannot be passed to program parts that are not allowed to  
modify the contents).


This makes it necessary for every part of the program that does not modify  
some parameter to declare this parameter const.


This makes it necessary for every user-defined type to define all its  
methods const that do not change its contents, that do not even make it  
possible to change its contents.


You cannot avoid clutter with these requirements.


Ciao
uwe
Jul 04 2005
prev sibling next sibling parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
(this is a formalization of the proposal I made before)

1) Reasonable model of "costness"

Preconditions:
a) Now it is already possible to implement "readonlyness"
for classes and structures "by hands" (see [1] and [2]) and
b) completely impossible to implement "readonlyness" for arrays(slices)
and pointers.

Proposal:

to introduce two new types: readonly array/slice
and readonly pointer.

readonly array/slice type has the same set of attributes
as array/slice except of 'mutating' methods: opIndexAssign
and length(int newLength)

readonly pointer cannot  be used for 'dereferncing
l-values' :
*readonly_pointer = something  // invalid opearation.

2) Notation.

As current proposal is limited only by
arrays and pointers cases then it is possible to
avoid use of any keyword like 'const',
'readonly' or 'fixed'.

Proposal: to use "readonly brackets" and "readonly star" - special
tokens like:
typename #[]
typename #*
or
typename []#
typename *#

3) How to avoid "clutter".

    Usually "const clutter" means [3]:
    a) "const is a pain to write everywhere,"
    b) "If I use it in one place, I have to use it all the time."

As given proposal is not forcing constness propagation
then b) problem is significantly less than as
with C++ const use cases.

Short notation (only one #) reduces a) case.
In any case one of the purposes of aliasing is to reduce
all sorts of clutters, e.g.:
     alias wchar#[] string;

---------------------------------------------------------------

[1] Ben Hinkle, MinTL:
struct Deque(..., bit ReadOnly = false, ...)
http://home.comcast.net/~benhinkle/mintl/

[2] Kris Bell, Mango Tree library
Dictionary, MutableDictionary
http://mango.dsource.org/Dictionary_8d-source.html

[3] Herb Sutter:
http://www.gotw.ca/publications/advice98.htm
Scott Meyers:
http://artima.com/intv/const.html
Jul 04 2005
prev sibling next sibling parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
Proposal also based on assumption that
readonly type is a base type having mutating methods
removed/disabled.

Idea is simple - to allow struct to derive from primitive types
(including struct itself). Having this it will be possible to
define readonly array as:

struct readonly(T:T[]): T[]
{
   private T opIndexAssign(T v, uint i) { return v; } // private -
                  // disabling it for the outer world.
   private uint length( uint nl ) { return length(); }  // private ...

   readonly opSlice() ....
   readonly opSlice(uint a, uint b) ....

}

so string (a.k.a. string-value) will look like as

alias readonly!(char[]) string;

----------------------------------------
openrj.d collections might look like

class Recordest
{
    private Field[] _fields;

    alias readonly!(Field[]) Fields;

    Fields fields() {  return cast(Fields) _fields;  }
}

-------------------------------------------
Question remains: what to do with pointers in this case?.
--------------------------------------------

Andrew. 
Jul 04 2005
prev sibling next sibling parent reply Nod <Nod_member pathlink.com> writes:
Heh, I can never resist a brainstorm; my mind thrives in such weather :)

NB: I have not followed this thread from the beginning, so there may be
rehashing, misunderstandings, and general unimplementability. Bash away.

1) Reasonable model of "costness"

The model I suggest is like the Delphi model, only slightly different. This model does not give us general constness, only constness for array contents, and only when passing function boundaries. I suggest a model where "constness" is only a contract like any other. The compiler does a best-effort to detect obvious violations at compile-time, and inserts thorough run-time checks for debug code. In this model, const violations can occur, but should be detected in the debugging phase. This model may seem limited, but I believe it to be sufficient. Remember, we are not trying to emulate strict hardware read-only-ness, we are only trying to avoid accidental alteration by code which does not "own" the data.
2) Notation. E.g. how readonly slice will look like
     in code. Function parameters, etc.

When defining a function, we must be able to set constness both on entry and return of the function. One could use the in/inout keywords to mean readonly/readwrite respectively, though this would be quite unintuitive on the return value. Defining the keywords ro/rw to use for this may be better. Example: char[rw] toUpper(char[rw] s) { ... } char[rw] toUpper(char[ro] s) { ... }
3) How to avoid "clutter". This term appears here
     not once - means that it is reasonable - so
     we need to deal with it.

Default to the common case. The ro/rw keywords are not required. If they are left out, we default to whichever is most common. char[] toUpper(char[] s) { ... } // uses defaults The common case can shift depending on context, and on whether we are receiving or returning the array. * When passing an array to a function, read-only is the common case. Thus, one has to explicitly use the "rw" keyword in the prototype if the function alters the array contents. * When returning arrays from functions, and the array was passed to us, the common case is that we can return it as read-write. As such, no "rw" keyword is necessary, in the general case. * When passing an array which is a member variable of the current class, it should be passed read-write between member functions. This list is not exhaustive, of course, but the rest of the defaults are only logical, e.g. always return a private member as read-only.
4) Please be as much brief as possible.

Well, at least I tried :) -Nod-
Jul 04 2005
parent reply xs0 <xs0 xs0.com> writes:
 When defining a function, we must be able to set constness both on entry and
 return of the function. One could use the in/inout keywords to mean
 readonly/readwrite respectively, though this would be quite unintuitive on the
 return value. Defining the keywords ro/rw to use for this may be better.
 
 Example:
 char[rw] toUpper(char[rw] s) { ... }
 char[rw] toUpper(char[ro] s) { ... }

I proposed this (with in/out/inout) some time ago, with not much response; anyhow, I still think it's a good idea, so +1 from me :) It may make sense to have write-only parameters, too..
3) How to avoid "clutter". This term appears here
    not once - means that it is reasonable - so
    we need to deal with it.

Default to the common case. The ro/rw keywords are not required. If they are left out, we default to whichever is most common. char[] toUpper(char[] s) { ... } // uses defaults The common case can shift depending on context, and on whether we are receiving or returning the array. * When passing an array to a function, read-only is the common case. Thus, one has to explicitly use the "rw" keyword in the prototype if the function alters the array contents.

Agreed.
 * When returning arrays from functions, and the array was passed to us, the
 common case is that we can return it as read-write. As such, no "rw" keyword is
 necessary, in the general case.

Not agreed. I think read-only arrays would be commonly returned, if such a facility existed. Furthermore, the COW principle suggests that it should be the consumer that .dups, not the producer. Even toUpper from above should not make a copy of the array, if it determines that all characters are upper-case already..
 * When passing an array which is a member variable of the current class, it
 should be passed read-write between member functions.

Why would you pass a member variable between member functions?
 This list is not exhaustive, of course, but the rest of the defaults are only
 logical, e.g. always return a private member as read-only.

Well, I think that the rules should be as simple as possible, so I'd say that the default for both function parameters and return values should be read-only (simply because it is the safest thing to do); the default for variables should be rw. xs0
Jul 05 2005
parent Nod <Nod_member pathlink.com> writes:
In article <dae7tv$9jv$1 digitaldaemon.com>, xs0 says...
 When defining a function, we must be able to set constness both on entry and
 return of the function. One could use the in/inout keywords to mean
 readonly/readwrite respectively, though this would be quite unintuitive on the
 return value. Defining the keywords ro/rw to use for this may be better.
 
 Example:
 char[rw] toUpper(char[rw] s) { ... }
 char[rw] toUpper(char[ro] s) { ... }

I proposed this (with in/out/inout) some time ago, with not much response; anyhow, I still think it's a good idea, so +1 from me :) It may make sense to have write-only parameters, too..

Yeah, I thought it wasn't really a new idea. +1 for you too then, mate :) I am not sure write-only parameters is needed though... in/out/inout does this job good enough, me thinks.
3) How to avoid "clutter". This term appears here
    not once - means that it is reasonable - so
    we need to deal with it.

Default to the common case. The ro/rw keywords are not required. If they are left out, we default to whichever is most common. char[] toUpper(char[] s) { ... } // uses defaults The common case can shift depending on context, and on whether we are receiving or returning the array. * When passing an array to a function, read-only is the common case. Thus, one has to explicitly use the "rw" keyword in the prototype if the function alters the array contents.

Agreed.

Woot!
 * When returning arrays from functions, and the array was passed to us, the
 common case is that we can return it as read-write. As such, no "rw" keyword is
 necessary, in the general case.

Not agreed. I think read-only arrays would be commonly returned, if such a facility existed. Furthermore, the COW principle suggests that it should be the consumer that .dups, not the producer. Even toUpper from above should not make a copy of the array, if it determines that all characters are upper-case already..

Ah yes, but you are thinking about arrays returned from class methods and such (see below). For regular functions, we can usually return it as rw since when a function exits, it immediately stops owning the array.
 * When passing an array which is a member variable of the current class, it
 should be passed read-write between member functions.

Why would you pass a member variable between member functions?

Heh, if one has a big list to choose from perhaps? Ok, that was a bad example, but the same would be true for an array passed into a inner class method, which happens a bit more often. The theory is that when arrays which are hidden by data encapsulation, i.e private, are passed into a scope in which they are normally not visible, they should by default be passed as ro. Conversely, while being passed within scopes which normally *does* have access, the default should be to pass it as rw. And no, I don't know how feasible it is for the compiler to keep track of all this :)
 This list is not exhaustive, of course, but the rest of the defaults are only
 logical, e.g. always return a private member as read-only.

Well, I think that the rules should be as simple as possible, so I'd say that the default for both function parameters and return values should be read-only (simply because it is the safest thing to do); the default for variables should be rw.

I agree that the rules should be as simple as possible, but one set of defaults just won't fit all situations. It's a subjective choice, of course, but I'd rather learn two sets of defaults, one for encapsulated data, and one for without, than have to explicitly specify rw-passing within all my classes.
xs0

-Nod-
Jul 05 2005
prev sibling next sibling parent Eugene Pelekhay <pelekhay gmail.com> writes:
Andrew Fedoniouk wrote:
 Let's say we decided that D needs
 read-only references and read-only slices
 (aka const references and const slices).
 

Assume that by default reference/slice/pointer is immutable : // mutable string : var char[] s1; : // immutable string : char[] s2; : // slices : s2 = s1; // legal : s1 = s2; // illegal : s2 = s1[1..2]; // legal : s1 = s2[1..2]; // illegal : s1 ~= s2; // legal : s2 ~= s1; // illegal : // function returns mutable string : var char[] func1(){} : // function returns immutable string : char[] func2(){} : // function with argument of immutable string : void func3(char[] s) {} : // function with argument of reference to immutable string : // reference assigned during function call and content can't be : // changed in caller trough returned reference : void func4(out char[] s) {} : // function with argument of initialized immutable string : // that reference can be changed during function call : void func5(inout char[] s) {} : // function with argument of mutable string : // content of string can be changed during function call : void func6(var char[] s) {} : // function with argument of reference to mutable string : // content of string can be changed outside of function : // through returned reference : void func7(out var char[] s) {} : // function with argument of reference to initialized mutable string : // content of string can be changed outside of function or during call : // through returned/passed reference : void func8(inout var char[] s) {} : : // slightly modified code from Regan : // news://news.digitalmars.com:119/opstczhfvz23k2f5 nrage.netwin.co.nz : void foo(char[] string) : var { : void* cmp = null; // defines variable for in/out contracts scope : } : in { : cmp = realloc(cmp,string.sizeof); : memcpy(cmp,&string,string.sizeof); : } : out { : assert(memcmp(cmp,&string,string.sizeof) == 0); : } : body { : //this causes the assert, remove it, no assert. : string.length = 20; : } : // for expression can be extented to support more than one type : // of variable : for(var int i=0; var float f=3.14; i<len; ++i) {}
Jul 05 2005
prev sibling next sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
1) Reasonable model of "costness"

Do what Walter suggested for 'in' plus 3 things: 1) 'out' return values 2) 'final' local variables and 3) make violations warn and not error. I think it should be a warning because mixing final/non-final is roughly like passing a signed int to an unsigned int - something fishy is going on but we'll assume the user knows what they are doing until they ask for our advice. In case it isn't obvious an explicit out return value means the output shouldn't be modified by the caller (ie assign it to a final variable or an in parameter). A final local variable is "deep immutable".
2) Notation. E.g. how readonly slice will look like
     in code. Function parameters, etc.

void foo(in char[] str); // Walter's idea out char[] foo(); // means output is read-only final char[] str = "blah"; // str is read-only
3) How to avoid "clutter". This term appears here
     not once - means that it is reasonable - so
     we need to deal with it.

Avoids clutter by making it a warning so you don't have to go nuts with final if you don't want to.
Jul 05 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Ben Hinkle" <Ben_member pathlink.com> wrote in message 
news:dadu5k$1ei$1 digitaldaemon.com...
1) Reasonable model of "costness"

Do what Walter suggested for 'in' plus 3 things: 1) 'out' return values 2) 'final' local variables and 3) make violations warn and not error. I think it should be a warning because mixing final/non-final is roughly like passing a signed int to an unsigned int - something fishy is going on but we'll assume the user knows what they are doing until they ask for our advice. In case it isn't obvious an explicit out return value means the output shouldn't be modified by the caller (ie assign it to a final variable or an in parameter). A final local variable is "deep immutable".
2) Notation. E.g. how readonly slice will look like
     in code. Function parameters, etc.

void foo(in char[] str); // Walter's idea out char[] foo(); // means output is read-only final char[] str = "blah"; // str is read-only
3) How to avoid "clutter". This term appears here
     not once - means that it is reasonable - so
     we need to deal with it.

Avoids clutter by making it a warning so you don't have to go nuts with final if you don't want to.

A variation on this is to just use the 'final' keyword and forget about marking output values. So an input parameter, local variable or field marked 'final' means "deep immutable" in Walter's sense for the lifetime of that variable. A final variable of array, pointer or reference type can be assigned new values but the contents of the array, pointer or reference (recursively) cannot change. void foo(final char[] str); // Walter's idea with 'final' instead of 'in' final char[] str = "blah"; // str contents are read-only struct Foo { final char[] str; } If one really misses const outputs a 'final' output value can be spoofed by transforming T foo(); to struct finalT{ final T x;} finalT foo(); although that would get annoying pretty quickly both to write, use and maintain.
Jul 11 2005
next sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:datvsm$1e46$1 digitaldaemon.com...
 "Ben Hinkle" <Ben_member pathlink.com> wrote in message 
 news:dadu5k$1ei$1 digitaldaemon.com...
1) Reasonable model of "costness"

Do what Walter suggested for 'in' plus 3 things: 1) 'out' return values 2) 'final' local variables and 3) make violations warn and not error. I think it should be a warning because mixing final/non-final is roughly like passing a signed int to an unsigned int - something fishy is going on but we'll assume the user knows what they are doing until they ask for our advice. In case it isn't obvious an explicit out return value means the output shouldn't be modified by the caller (ie assign it to a final variable or an in parameter). A final local variable is "deep immutable".
2) Notation. E.g. how readonly slice will look like
     in code. Function parameters, etc.

void foo(in char[] str); // Walter's idea out char[] foo(); // means output is read-only final char[] str = "blah"; // str is read-only
3) How to avoid "clutter". This term appears here
     not once - means that it is reasonable - so
     we need to deal with it.

Avoids clutter by making it a warning so you don't have to go nuts with final if you don't want to.

A variation on this is to just use the 'final' keyword and forget about marking output values. So an input parameter, local variable or field marked 'final' means "deep immutable" in Walter's sense for the lifetime of that variable. A final variable of array, pointer or reference type can be assigned new values but the contents of the array, pointer or reference (recursively) cannot change. void foo(final char[] str); // Walter's idea with 'final' instead of 'in' final char[] str = "blah"; // str contents are read-only struct Foo { final char[] str; } If one really misses const outputs a 'final' output value can be spoofed by transforming T foo(); to struct finalT{ final T x;} finalT foo(); although that would get annoying pretty quickly both to write, use and maintain.

Ben, am I correct thinking that your proposal void foo(final char[] str); // Walter's idea with 'final' instead of 'in' final char[] str = "blah"; // str contents are read-only struct Foo { final char[] str; } uses the same idea as in C++ void foo(const char[] str); const char[] str = "blah"; struct Foo { const char[] str; } but with different keyword?
Jul 11 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 Ben, am I correct thinking that your proposal

 void foo(final char[] str); // Walter's idea with 'final' instead of 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

 uses the same idea as in C++

 void foo(const char[] str);
 const char[] str = "blah";
 struct Foo { const char[] str; }

 but with different keyword?

not exactly - see Walter's post about 'in'. Also in C++ const is a type modifier and here final would modify a variable like in Java. For information about Java's notion of final see http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.12.4 The difference from Java's final is that in Java final means the variable doesn't change but the array or object contents can change and here it would be the reverse - the variable can change but the contents can't. Other than that it's the same as Java's final :-)
Jul 11 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:dau6b2$1kuv$1 digitaldaemon.com...
 Ben, am I correct thinking that your proposal

 void foo(final char[] str); // Walter's idea with 'final' instead of 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

 uses the same idea as in C++

 void foo(const char[] str);
 const char[] str = "blah";
 struct Foo { const char[] str; }

 but with different keyword?

not exactly - see Walter's post about 'in'. Also in C++ const is a type modifier and here final would modify a variable like in Java. For information about Java's notion of final see http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.12.4 The difference from Java's final is that in Java final means the variable doesn't change but the array or object contents can change and here it would be the reverse - the variable can change but the contents can't. Other than that it's the same as Java's final :-)

Hmm, it seems that we are speaking about the same entity but using different terms. See, in C++ declaration of const variable like const typename v; means declaration of variable of "const typename" - implicitly generated type - const version of typename with all non const members disabled ( non availble ) for use through 'v' alias. Therefore, if I understand you and Walter correctly C++: void foo(const char[] str) is exactly yours: void foo(final char[] str) and Walter's: void foo(in char[] str) Is this correct? Andrew.
Jul 11 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly generated 
 type - const version
 of typename with all non const members disabled ( non availble ) for use 
 through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here. In addition the final/in proposal is different than C++ const for the following example: struct A { A* ptr; } void foo(final A* ptr2) { ptr2.ptr.ptr = null; // illegal } void main() { A a; a.ptr = &a; foo(&a); } vs in C++ struct A { A* ptr; } void foo(const A* ptr2) { ptr2->ptr->ptr = 0; // legal } void main() { A a; a.ptr = &a; foo(&a); } The example is simplistic but it illustrates the meaning of recursive/deep immutability.
Jul 11 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:daubr5$1phd$1 digitaldaemon.com...
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly generated 
 type - const version
 of typename with all non const members disabled ( non availble ) for use 
 through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here.

Practical "ultimate resource immutability" ( a.k.a. "Andrei's deep immutability" ) implementation is not feasible on compiler level for langages of D class (e.g. having pointers). Only on VM level it is possible to do it and at some extent only. As "D virtual machine" is a processor itself then it means that without os/hardware support implementation of this dream is not possible. I though that it was clear from the very beginning.... BTW: Do you know any solution for read-only protection of any arbitrary memory location? Highly probable that I missed something in this area recently.
 In addition the final/in proposal is different than C++ const for the 
 following example:
  struct A {
    A* ptr;
  }
  void foo(final A* ptr2) {
    ptr2.ptr.ptr = null; // illegal
  }
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }

 vs in C++
  struct A {
    A* ptr;
  }
  void foo(const A* ptr2) {
    ptr2->ptr->ptr = 0; // legal
  }

True. I think that C++ behavior in this case can be better as if you would change this to: struct A { A *_ptr; A* ptr() { return _ptr; } }; void foo(const A* ptr2) { ptr2->ptr()->ptr(); // error here } you will get an error: 'ptr' : cannot convert 'this' pointer from 'const struct A' to 'struct A &' or so.
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }
 The example is simplistic but it illustrates the meaning of recursive/deep 
 immutability.

Yes it is. As I have shown in example above C++ does such deep immutability. I am using "deep immutability" term as it is described here: http://www-sop.inria.fr/everest/events/cassis05/Transp/poll.pdf page #18
Jul 11 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
news:daui3u$1vos$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daubr5$1phd$1 digitaldaemon.com...
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly generated 
 type - const version
 of typename with all non const members disabled ( non availble ) for use 
 through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here.

Practical "ultimate resource immutability" ( a.k.a. "Andrei's deep immutability" ) implementation is not feasible on compiler level for langages of D class (e.g. having pointers). Only on VM level it is possible to do it and at some extent only. As "D virtual machine" is a processor itself then it means that without os/hardware support implementation of this dream is not possible. I though that it was clear from the very beginning.... BTW: Do you know any solution for read-only protection of any arbitrary memory location? Highly probable that I missed something in this area recently.

As Walter's original post said deep immutability is a contract made by the programmer to the compiler (and other programmers) which the compiler can verify in simple cases similar to the cases C++ const would catch. It is independent of any VM or the existence of pointers. Are you trying to say the contract must be verfied in *all* cases? I agree it is impractical to validate all cases. I suspect that's why Walter said the compiler would only catch simple cases like assigning through an immutable pointer and such.
 In addition the final/in proposal is different than C++ const for the 
 following example:
  struct A {
    A* ptr;
  }
  void foo(final A* ptr2) {
    ptr2.ptr.ptr = null; // illegal
  }
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }

 vs in C++
  struct A {
    A* ptr;
  }
  void foo(const A* ptr2) {
    ptr2->ptr->ptr = 0; // legal
  }

True. I think that C++ behavior in this case can be better as if you would change this to: struct A { A *_ptr; A* ptr() { return _ptr; } }; void foo(const A* ptr2) { ptr2->ptr()->ptr(); // error here } you will get an error: 'ptr' : cannot convert 'this' pointer from 'const struct A' to 'struct A &' or so.
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }
 The example is simplistic but it illustrates the meaning of 
 recursive/deep immutability.

Yes it is. As I have shown in example above C++ does such deep immutability.

Sure - if you change the C++ example to not allow any writing then it becomes immutable. But that is different than what I originally posted and what Walter meant by deep immutable parameter.
 I am using "deep immutability" term as it is described here:
 http://www-sop.inria.fr/everest/events/cassis05/Transp/poll.pdf
 page #18

I understand. Do you understand Walter's description of deep immutability from his post? The key phrase in Walter's post is "every sub-object reachable from that parameter". Perhaps we should start using the phrases "deep immutable type" and "deep immutable variable" to distinguish between the notions.
Jul 11 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:daul84$229o$1 digitaldaemon.com...
 "Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
 news:daui3u$1vos$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daubr5$1phd$1 digitaldaemon.com...
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly 
 generated type - const version
 of typename with all non const members disabled ( non availble ) for 
 use through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here.

Practical "ultimate resource immutability" ( a.k.a. "Andrei's deep immutability" ) implementation is not feasible on compiler level for langages of D class (e.g. having pointers). Only on VM level it is possible to do it and at some extent only. As "D virtual machine" is a processor itself then it means that without os/hardware support implementation of this dream is not possible. I though that it was clear from the very beginning.... BTW: Do you know any solution for read-only protection of any arbitrary memory location? Highly probable that I missed something in this area recently.

As Walter's original post said deep immutability is a contract made by the programmer to the compiler (and other programmers) which the compiler can verify in simple cases similar to the cases C++ const would catch. It is independent of any VM or the existence of pointers. Are you trying to say the contract must be verfied in *all* cases? I agree it is impractical to validate all cases. I suspect that's why Walter said the compiler would only catch simple cases like assigning through an immutable pointer and such.

I suspect quite opposite: Walter: "On the other hand, look at C++ "const". Nothing about "const" says that some other thread or reference cannot change the value out from under you at any moment. Furthermore, it can be cast away and modified anyway. The semantic value of C++ "const" is essentially zip. This is why I am often flummoxed why it is given such weight in C++." Ben: "Are you trying to say the contract must be verfied in *all* cases?" Of course no. This is Walter who is trying to say this as far as I understand his concerns about 'const' in C++.
 In addition the final/in proposal is different than C++ const for the 
 following example:
  struct A {
    A* ptr;
  }
  void foo(final A* ptr2) {
    ptr2.ptr.ptr = null; // illegal
  }
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }

 vs in C++
  struct A {
    A* ptr;
  }
  void foo(const A* ptr2) {
    ptr2->ptr->ptr = 0; // legal
  }

True. I think that C++ behavior in this case can be better as if you would change this to: struct A { A *_ptr; A* ptr() { return _ptr; } }; void foo(const A* ptr2) { ptr2->ptr()->ptr(); // error here } you will get an error: 'ptr' : cannot convert 'this' pointer from 'const struct A' to 'struct A &' or so.
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }
 The example is simplistic but it illustrates the meaning of 
 recursive/deep immutability.

Yes it is. As I have shown in example above C++ does such deep immutability.

Sure - if you change the C++ example to not allow any writing then it becomes immutable. But that is different than what I originally posted and what Walter meant by deep immutable parameter.
 I am using "deep immutability" term as it is described here:
 http://www-sop.inria.fr/everest/events/cassis05/Transp/poll.pdf
 page #18

I understand. Do you understand Walter's description of deep immutability from his post? The key phrase in Walter's post is "every sub-object reachable from that parameter". Perhaps we should start using the phrases "deep immutable type" and "deep immutable variable" to distinguish between the notions.

Yes, I think I understand "every sub-object reachable from that parameter". This is exactly referentional deep immutability - "using this particular reference to the object it is impossible to change state of the object and every sub-object reachable from that object" This is what C++ and Javari are doing: "The specific constraint expressed is that the abstract state of the object to which an immutable reference refers cannot be modified using that reference. The abstract state is (part of) the transitively reachable state: that is, the state of the object and all state reachable from it by following references. The type system permits explicitly excluding fields or objects from the abstract state of an object. For a statically type-safe language, the type system guarantees reference immutability." Javari also allows to: "If the language is extended with immutability downcasts, then run-time checks enforce the reference immutability constraints." by making specific changes in VM. C++ is using 'const' and Javari uses 'readonly' for marking parameters, fields and methods. http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004-slides.pdf http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004.pdf
Jul 11 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 11 Jul 2005 16:09:37 -0700, Andrew Fedoniouk  
<news terrainformatica.com> wrote:
 Yes, I think I understand "every sub-object reachable from that  
 parameter".
 This is exactly referentional deep immutability -
 "using this particular reference to the object it is impossible to change
 state of the object and every sub-object reachable from that object"

 This is what C++ and Javari are doing:

Doesn't this example: struct A { A* ptr; } void foo(const A* ptr2) { ptr2->ptr->ptr = 0; // legal } void main() { A a; a.ptr = &a; foo(&a); } show that C++ const does not (by default) protect sub-objects, instead a re-design (as you posted) to also use a getter is required. Walters 'in'/'final' deep immutable idea would not require the getter (if I understand it correctly) and so is surely superior? Regan
Jul 11 2005
parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opstru1ah323k2f5 nrage.netwin.co.nz...
 On Mon, 11 Jul 2005 16:09:37 -0700, Andrew Fedoniouk 
 <news terrainformatica.com> wrote:
 Yes, I think I understand "every sub-object reachable from that 
 parameter".
 This is exactly referentional deep immutability -
 "using this particular reference to the object it is impossible to change
 state of the object and every sub-object reachable from that object"

 This is what C++ and Javari are doing:

Doesn't this example: struct A { A* ptr; } void foo(const A* ptr2) { ptr2->ptr->ptr = 0; // legal } void main() { A a; a.ptr = &a; foo(&a); } show that C++ const does not (by default) protect sub-objects, instead a re-design (as you posted) to also use a getter is required.

As far as I recall it was some rationale behind this. I'll try to find. In any case implementation of this approach will end up with marking parameters, fields and methods by 'const' or 'readonly'. This is what is being considered by Walter as "cluttering" (I personally don't think so)
 Walters 'in'/'final' deep immutable idea would not require the getter (if 
 I understand it correctly) and so is surely superior?

 Regan 

Jul 11 2005
prev sibling next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
news:dauu7h$2a8i$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daul84$229o$1 digitaldaemon.com...
 "Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
 news:daui3u$1vos$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daubr5$1phd$1 digitaldaemon.com...
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly 
 generated type - const version
 of typename with all non const members disabled ( non availble ) for 
 use through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here.

Practical "ultimate resource immutability" ( a.k.a. "Andrei's deep immutability" ) implementation is not feasible on compiler level for langages of D class (e.g. having pointers). Only on VM level it is possible to do it and at some extent only. As "D virtual machine" is a processor itself then it means that without os/hardware support implementation of this dream is not possible. I though that it was clear from the very beginning.... BTW: Do you know any solution for read-only protection of any arbitrary memory location? Highly probable that I missed something in this area recently.

As Walter's original post said deep immutability is a contract made by the programmer to the compiler (and other programmers) which the compiler can verify in simple cases similar to the cases C++ const would catch. It is independent of any VM or the existence of pointers. Are you trying to say the contract must be verfied in *all* cases? I agree it is impractical to validate all cases. I suspect that's why Walter said the compiler would only catch simple cases like assigning through an immutable pointer and such.

I suspect quite opposite: Walter: "On the other hand, look at C++ "const". Nothing about "const" says that some other thread or reference cannot change the value out from under you at any moment. Furthermore, it can be cast away and modified anyway. The semantic value of C++ "const" is essentially zip. This is why I am often flummoxed why it is given such weight in C++." Ben: "Are you trying to say the contract must be verfied in *all* cases?" Of course no. This is Walter who is trying to say this as far as I understand his concerns about 'const' in C++.

I can only assume you haven't read all of Walter's post or that there is some language barrier going on here. Let me try to expand on what Walter said. The use of 'in' or 'final' is a promise made by the *programmer* to the compiler to obey the contract. The compiler can validate the contract in a subset of circumstances. The compiler will not and most likely cannot validate the contract in all cases. The whole point is that in/final is a promise by the programmer that nothing will change the value and is independent of what the compiler will or will not be able to validate.
 In addition the final/in proposal is different than C++ const for the 
 following example:
  struct A {
    A* ptr;
  }
  void foo(final A* ptr2) {
    ptr2.ptr.ptr = null; // illegal
  }
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }

 vs in C++
  struct A {
    A* ptr;
  }
  void foo(const A* ptr2) {
    ptr2->ptr->ptr = 0; // legal
  }

True. I think that C++ behavior in this case can be better as if you would change this to: struct A { A *_ptr; A* ptr() { return _ptr; } }; void foo(const A* ptr2) { ptr2->ptr()->ptr(); // error here } you will get an error: 'ptr' : cannot convert 'this' pointer from 'const struct A' to 'struct A &' or so.
  void main() {
    A a;
    a.ptr = &a;
    foo(&a);
  }
 The example is simplistic but it illustrates the meaning of 
 recursive/deep immutability.

Yes it is. As I have shown in example above C++ does such deep immutability.

Sure - if you change the C++ example to not allow any writing then it becomes immutable. But that is different than what I originally posted and what Walter meant by deep immutable parameter.
 I am using "deep immutability" term as it is described here:
 http://www-sop.inria.fr/everest/events/cassis05/Transp/poll.pdf
 page #18

I understand. Do you understand Walter's description of deep immutability from his post? The key phrase in Walter's post is "every sub-object reachable from that parameter". Perhaps we should start using the phrases "deep immutable type" and "deep immutable variable" to distinguish between the notions.

Yes, I think I understand "every sub-object reachable from that parameter". This is exactly referentional deep immutability - "using this particular reference to the object it is impossible to change state of the object and every sub-object reachable from that object"

The key difference is that const and Javari encode the "immutability" in the type and the in/final semantics are not encoded in the type (hence my original statement that final/in aren't type modifiers). Please consider again the examples I posted comparing final and const "deep immutability".
 This is what C++ and Javari are doing:

 "The specific constraint expressed is that the abstract state of the 
 object to which an immutable reference refers cannot be modified using 
 that reference. The abstract state is (part of) the transitively reachable 
 state: that is, the state of the object and all state reachable from it by 
 following references. The type system permits explicitly excluding fields 
 or objects from the abstract state of an object. For a statically 
 type-safe language, the type system guarantees reference immutability."
 Javari also allows to:
 "If the language is extended with immutability downcasts, then run-time 
 checks enforce the reference immutability constraints."
 by making specific changes in VM.

 C++ is using 'const' and Javari uses 'readonly' for marking parameters, 
 fields and methods.
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004-slides.pdf
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004.pdf

Walter's post as I understand it avoids using the type system or run-time checks to attempt to enforce immutability. Note the key phrase in the first sentance you quote: "cannot be modified using that reference". Walter is trying to find a solution where that would be replaced with "cannot be modified".
Jul 11 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:dav7rm$2hp9$1 digitaldaemon.com...
 "Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
 news:dauu7h$2a8i$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daul84$229o$1 digitaldaemon.com...
 "Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
 news:daui3u$1vos$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:daubr5$1phd$1 digitaldaemon.com...
 Hmm, it seems that we are speaking about the same entity but using
 different terms.

 See, in C++ declaration of const variable like

 const typename v;

 means declaration of variable of "const typename" - implicitly 
 generated type - const version
 of typename with all non const members disabled ( non availble ) for 
 use through 'v' alias.

 Therefore, if I understand you and Walter correctly

 C++:
      void foo(const char[] str)

 is exactly yours:
      void foo(final char[] str)

 and Walter's:
      void foo(in char[] str)

 Is this correct?

Not as I read Walter's posts like digitalmars.D/26096. In particular C++ const says nothing about changing the values through other references while this final/in proposal would assert that the value remains constant through all references. That's been the drum-beat of all these posts saying why D doesn't have const already. I don't understand the confusion here.

Practical "ultimate resource immutability" ( a.k.a. "Andrei's deep immutability" ) implementation is not feasible on compiler level for langages of D class (e.g. having pointers). Only on VM level it is possible to do it and at some extent only. As "D virtual machine" is a processor itself then it means that without os/hardware support implementation of this dream is not possible. I though that it was clear from the very beginning.... BTW: Do you know any solution for read-only protection of any arbitrary memory location? Highly probable that I missed something in this area recently.

As Walter's original post said deep immutability is a contract made by the programmer to the compiler (and other programmers) which the compiler can verify in simple cases similar to the cases C++ const would catch. It is independent of any VM or the existence of pointers. Are you trying to say the contract must be verfied in *all* cases? I agree it is impractical to validate all cases. I suspect that's why Walter said the compiler would only catch simple cases like assigning through an immutable pointer and such.

I suspect quite opposite: Walter: "On the other hand, look at C++ "const". Nothing about "const" says that some other thread or reference cannot change the value out from under you at any moment. Furthermore, it can be cast away and modified anyway. The semantic value of C++ "const" is essentially zip. This is why I am often flummoxed why it is given such weight in C++." Ben: "Are you trying to say the contract must be verfied in *all* cases?" Of course no. This is Walter who is trying to say this as far as I understand his concerns about 'const' in C++.

I can only assume you haven't read all of Walter's post or that there is some language barrier going on here. Let me try to expand on what Walter said. The use of 'in' or 'final' is a promise made by the *programmer* to the compiler to obey the contract. The compiler can validate the contract in a subset of circumstances. The compiler will not and most likely cannot validate the contract in all cases. The whole point is that in/final is a promise by the programmer that nothing will change the value and is independent of what the compiler will or will not be able to validate.

Ben, honestly I don't think that any language barrier is involved here. Probably different thinking context or particular design environment or tasks. Anyway Let me try to reproduce of what I understand so far, (I will cite this): digitalmars.D/26096 "(#1) Deep immutable parameters would have unchanging values for the scope of that parameter, and also every sub-object reachable by that parameter would also be unchangeable. (#2)The values wouldn't change even by another reference or another thread. (This is quite unlike C++ "const".) (#3) "in" would be a promise by the programmer, and could not be guaranteed by the compiler (#4) - but - the optimizer can take advantage of this promise to generate perhaps significantly better code. (#5) (With C++ "const", values can change at any moment by another reference or another thread, making optimizations based on "const" impossible.) I understand it as: Programmer will try to write code of function with 'in' parameters without side effects - he is expressing following good wishes (#1) "I am not going to change here anything", (#2) "I swear you that I did my best to do not use other references and threads to modify everything which is reachable from this 'in' parameter". (#3) Compiler in its turn will do its best to verify promises given in #1 and #2 but without any warranties as it just impossible in CT. (#4) Based on promises given above (mean that they can and will be false as we are humans) optimizer will try to "generate significantly better code". And statement #5 is telling that this sequence of gentlemen agreements is significantly better for optimization purposes than 'const' agreements used in C++ (which are also gentlemen agreements). Is this correct? (Sorry, probably it sounds sarcastic, but this is how I am getting it, nothing really personal, I swear you :)
 This is exactly referentional deep immutability -
 "using this particular reference to the object it is impossible to change
 state of the object and every sub-object reachable from that object"

The key difference is that const and Javari encode the "immutability" in the type and the in/final semantics are not encoded in the type (hence my original statement that final/in aren't type modifiers). Please consider again the examples I posted comparing final and const "deep immutability".

Let's imagine that I have immutable collection of mutable objects e.g. values of record in recordset - I can change values but not collection itself. Is it reasonable construction? To use this system practically we need "mutable" keyword or such. In given record-buffer example I will need something like this: void store( in mutable value[] recordValues)
 This is what C++ and Javari are doing:

 "The specific constraint expressed is that the abstract state of the 
 object to which an immutable reference refers cannot be modified using 
 that reference. The abstract state is (part of) the transitively 
 reachable state: that is, the state of the object and all state reachable 
 from it by following references. The type system permits explicitly 
 excluding fields or objects from the abstract state of an object. For a 
 statically type-safe language, the type system guarantees reference 
 immutability."
 Javari also allows to:
 "If the language is extended with immutability downcasts, then run-time 
 checks enforce the reference immutability constraints."
 by making specific changes in VM.

 C++ is using 'const' and Javari uses 'readonly' for marking parameters, 
 fields and methods.
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004-slides.pdf
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004.pdf

Walter's post as I understand it avoids using the type system or run-time checks to attempt to enforce immutability. Note the key phrase in the first sentance you quote: "cannot be modified using that reference". Walter is trying to find a solution where that would be replaced with "cannot be modified".

I understand it as: Walter is trying to find verification algorithm which allows to prove that any given object passed through 'in reference' will not be modified by any other reference inside and outside of the function and in other threads. And intention is to do this without modification of type system and without implementation of runtime checks and without any OS/hardware support. And all this above supposed to work reliable with slices and those "free range chickens" named pointers.... Did I get it right? Sounds like "perpetuum mobile" project to be honest. Moreover I believe this problem is close to the Halting Problem of Turing Machine (which was proved to be unsolvable). I think it is fundamentally impossible to prove real immutability of any given memory location at compile time without type constraints. If it would be possible then you will not need GC at all as you will be able to delete exact set of unused resources at any arbitrary source line of your program. This is how I understand it. I hope that I am wrong somewhere here. Andrew Fedoniouk. http://terrainformatica.com
Jul 12 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 Let me try to reproduce of what I understand so far, (I will cite this):
 digitalmars.D/26096

 "(#1) Deep immutable parameters would have unchanging values for the scope 
 of that
 parameter, and also every sub-object reachable by that parameter would 
 also
 be unchangeable.
 (#2)The values wouldn't change even by another reference or
 another thread. (This is quite unlike C++ "const".)
 (#3) "in" would be a promise by the programmer, and could not be 
 guaranteed by the compiler
 (#4) - but - the optimizer can take advantage of this promise to generate 
 perhaps
 significantly better code.
 (#5) (With C++ "const", values can change at any moment by another 
 reference or another thread,
 making optimizations based on "const" impossible.)

 I understand it as:
 Programmer will try to write code of function with 'in' parameters
 without side effects - he is expressing following good wishes
 (#1) "I am not going to change here anything",
 (#2) "I swear you that I did my best to do not use other references
 and threads to modify everything which is reachable from
 this 'in' parameter".
 (#3) Compiler in its turn will do its best to verify promises given
 in #1 and #2 but without any warranties as it just impossible in CT.
 (#4) Based on promises given above (mean that they can and
 will be false as we are humans) optimizer will try to
 "generate significantly better code".
 And statement #5 is telling that this sequence of gentlemen
 agreements is significantly better for optimization purposes
 than 'const' agreements used in C++ (which are also gentlemen agreements).

 Is this correct?

yes - that's how I read it, too.
 (Sorry, probably it sounds sarcastic, but this is how I am getting it,
 nothing really personal, I swear you :)

 This is exactly referentional deep immutability -
 "using this particular reference to the object it is impossible to 
 change
 state of the object and every sub-object reachable from that object"

The key difference is that const and Javari encode the "immutability" in the type and the in/final semantics are not encoded in the type (hence my original statement that final/in aren't type modifiers). Please consider again the examples I posted comparing final and const "deep immutability".

Let's imagine that I have immutable collection of mutable objects e.g. values of record in recordset - I can change values but not collection itself. Is it reasonable construction? To use this system practically we need "mutable" keyword or such. In given record-buffer example I will need something like this: void store( in mutable value[] recordValues)

The in/final proposal would not allow immutable collections of mutable objects - either everything is immutable or nothing is. If it is decided that is important then I agree either another keyword is needed or deep immutability needs to be dropped.
 This is what C++ and Javari are doing:

 "The specific constraint expressed is that the abstract state of the 
 object to which an immutable reference refers cannot be modified using 
 that reference. The abstract state is (part of) the transitively 
 reachable state: that is, the state of the object and all state 
 reachable from it by following references. The type system permits 
 explicitly excluding fields or objects from the abstract state of an 
 object. For a statically type-safe language, the type system guarantees 
 reference immutability."
 Javari also allows to:
 "If the language is extended with immutability downcasts, then run-time 
 checks enforce the reference immutability constraints."
 by making specific changes in VM.

 C++ is using 'const' and Javari uses 'readonly' for marking parameters, 
 fields and methods.
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004-slides.pdf
 http://pag.csail.mit.edu/~mernst/pubs/ref-immutability-oopsla2004.pdf

Walter's post as I understand it avoids using the type system or run-time checks to attempt to enforce immutability. Note the key phrase in the first sentance you quote: "cannot be modified using that reference". Walter is trying to find a solution where that would be replaced with "cannot be modified".

I understand it as: Walter is trying to find verification algorithm which allows to prove that any given object passed through 'in reference' will not be modified by any other reference inside and outside of the function and in other threads. And intention is to do this without modification of type system and without implementation of runtime checks and without any OS/hardware support. And all this above supposed to work reliable with slices and those "free range chickens" named pointers.... Did I get it right?

I didn't get the impression that is holding him up. He explicitly says the verification algorithm can be as weak as the compiler author wants - in particular the verfication algorithm can be "always trust the programmer and check nothing". Maybe he is working on a fancy verification algorithm - who knows. I hope he isn't spending much time on it. I think he's mulling over designs to balance the trade-offs between C++ const use-cases, deep immutability and optimization opportunities. The general design is more important to get right than the verification algorithm.
Jul 12 2005
parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
 Let's imagine that I have immutable collection of mutable objects e.g.
 values of record in recordset - I can change values but not collection 
 itself.
 Is it reasonable construction?

 To use this system practically we need "mutable" keyword or such.
 In given record-buffer example I will need something like this:

 void store( in mutable value[] recordValues)

The in/final proposal would not allow immutable collections of mutable objects - either everything is immutable or nothing is. If it is decided that is important then I agree either another keyword is needed or deep immutability needs to be dropped.

As more I am looking into the problem as more I like simple solution of having read-only arrays and read-only pointers. It is a deterministic and natural solution - can be verified and enforced in 100% cases. Combined with use of existing access specifiers private/public/etc. will be more powerfull than 'const' and yet simple.
Jul 12 2005
prev sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 This is what C++ and Javari are doing:

 "The specific constraint expressed is that the abstract state of the 
 object to which an immutable reference refers cannot be modified using 
 that reference. The abstract state is (part of) the transitively reachable 
 state: that is, the state of the object and all state reachable from it by 
 following references. The type system permits explicitly excluding fields 
 or objects from the abstract state of an object. For a statically 
 type-safe language, the type system guarantees reference immutability."

sorry for the double post but I should add that C++'s const is not transitive, as illustrated by the code example I posted before.
Jul 11 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 A variation on this is to just use the 'final' keyword and forget about 
 marking output values. So an input parameter, local variable or field 
 marked 'final' means "deep immutable" in Walter's sense for the lifetime 
 of that variable. A final variable of array, pointer or reference type can 
 be assigned new values but the contents of the array, pointer or reference 
 (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.
Jul 13 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:db3ejf$i7u$1 digitaldaemon.com...
 A variation on this is to just use the 'final' keyword and forget about 
 marking output values. So an input parameter, local variable or field 
 marked 'final' means "deep immutable" in Walter's sense for the lifetime 
 of that variable. A final variable of array, pointer or reference type 
 can be assigned new values but the contents of the array, pointer or 
 reference (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.

Yep, in/out is just byval/byref and has nothing common with constness. BTW: You've provided example which has little sense :) void foo(final inout BigHonkinStruct x) As 'inout' tells us - information will flow in and out but foo cannot modify x so it is only 'in' in fact. But with immutable arrays and pointers in/out has perfect sense: void foo(in BigHonkinStruct*# x) // passing x by reference // struct pointed by x will not be modified inside // foo void foo(out BigHonkinStruct*# x) // returning reference to // const struct void foo(inout BigHonkinStruct*# x) // reading some const struct and // returning reference to new const struct Andrew.
Jul 13 2005
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 BTW: You've provided example which has little sense :)

 void foo(final inout BigHonkinStruct x)

 As 'inout' tells us - information will flow in and out
 but foo cannot modify x so it is only 'in' in fact.

inout is used often in D to get pass-by-reference semantics without intending to modify the contents.
Jul 13 2005
parent "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:db3hbg$ke8$1 digitaldaemon.com...
 BTW: You've provided example which has little sense :)

 void foo(final inout BigHonkinStruct x)

 As 'inout' tells us - information will flow in and out
 but foo cannot modify x so it is only 'in' in fact.

inout is used often in D to get pass-by-reference semantics without intending to modify the contents.

I know. And is it good? It is breaking basic DbC rules I guess. In any case foo(in int* var) / foo(in int*# var) tells me more than: foo(inout int var)
Jul 13 2005
prev sibling parent reply Dave <Dave_member pathlink.com> writes:
In article <db3g4b$jdj$1 digitaldaemon.com>, Andrew Fedoniouk says...
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message 
news:db3ejf$i7u$1 digitaldaemon.com...
 A variation on this is to just use the 'final' keyword and forget about 
 marking output values. So an input parameter, local variable or field 
 marked 'final' means "deep immutable" in Walter's sense for the lifetime 
 of that variable. A final variable of array, pointer or reference type 
 can be assigned new values but the contents of the array, pointer or 
 reference (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.

Yep, in/out is just byval/byref and has nothing common with constness. BTW: You've provided example which has little sense :) void foo(final inout BigHonkinStruct x) As 'inout' tells us - information will flow in and out but foo cannot modify x so it is only 'in' in fact.

With Walter's 'explicit in' idea, then having 'inout' "double" as a way to pass a byval variable (greater in size than a machine word more efficiently) wouldn't be needed - the compiler could easily decide that and just do the optimization. This can be more efficient with something even as small as a long or a double prec. fp var. on 32 bit machines. so instead of 'void foo(final inout BigHonkinStruct x)' or 'void foo(in BigHonkinStruct*# x)' it would just be: 'void foo(in BigHonkinStruct x)' With that in mind, I *still* say both 'implicit in' and 'explicit in' should be changed to act like Walter's proposal because that is what is expected in 99% of the cases anyhow, and the other 1% is when the programmer forgot the 'out' or 'inout' with byref vars. If the compiler detects even the simple cases, then most of the remaining 1% wouldn't compile anyway and the programmer would simply have to change it to 'out' or 'inout' as it should have been in the first place <g>. The other issue with using 'inout' as a substitute for '&' is that you can't pass a temporary: struct S { int x, y, z; } S initAnS(int x, int y, int z) { S s; s.x = x, s.y = y, s.z = z; return s; } int foo(inout S s) { return s.x * s.y * s.z; } void main() { int i = foo(initAnS(10,20,30)); // Error: initAnS(10,20,30) is not an lvalue } The optimizations aren't my biggest deal though -- IMHO, the param. storage specifiers should mean what they say or imply, it's just that the semantics of 'in' (both implicit and explicit) need to change to be like the const or 'readonly' vars. that we've all been talking about and then perhaps (if needed) a contrary keyword like 'mutable' could be added. Will an 'immutable implicit in' be confusing for C/++ people at first? Perhaps, but not after the compiler spits out an error for the first few trivial cases - then it will just be "Ok, if I want to modify a function parameter, I have to specify 'out' or 'inout' or make a copy. Maybe it won't be confusing at all because in C-land they have to use a pointer and in C++ land they have to use '&' so maybe making 'implicit in' act like a byval for even byref parameters will come more naturally and it's the VB, C# and Java people wo will be confused at first. I think Walter had it right on his first pass here: digitalmars.D/26096 - Dave
Jul 13 2005
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Dave" <Dave_member pathlink.com> wrote in message 
news:db3pfk$rje$1 digitaldaemon.com...
 In article <db3g4b$jdj$1 digitaldaemon.com>, Andrew Fedoniouk says...
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:db3ejf$i7u$1 digitaldaemon.com...
 A variation on this is to just use the 'final' keyword and forget about
 marking output values. So an input parameter, local variable or field
 marked 'final' means "deep immutable" in Walter's sense for the 
 lifetime
 of that variable. A final variable of array, pointer or reference type
 can be assigned new values but the contents of the array, pointer or
 reference (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 
 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.

Yep, in/out is just byval/byref and has nothing common with constness. BTW: You've provided example which has little sense :) void foo(final inout BigHonkinStruct x) As 'inout' tells us - information will flow in and out but foo cannot modify x so it is only 'in' in fact.

With Walter's 'explicit in' idea, then having 'inout' "double" as a way to pass a byval variable (greater in size than a machine word more efficiently) wouldn't be needed - the compiler could easily decide that and just do the optimization.

It can? How about an example like struct BigHonkinStruct { int a; } //ok not so honkin but you get the idea void foo(in BigHonkinStruct x, void delegate() cb) { int old = x.a; cb(); assert( x.a == old ); } void bar() { BigHonkinStruct y; void baz(){y.a = 10;} foo(y,&baz); } Pass-by-value semantics means that the x inside foo is (semantically) a copy of y so the function bar must be able to change y whenever it wants and it has no impact on x. Hence you can't pass y by reference to foo even though it is declared as 'explicit in'. Are you thinking of something else? I might be mis-reading your point.
 This can be more efficient with something even as small as a long or a 
 double
 prec. fp var. on 32 bit machines.

 so instead of

 'void foo(final inout BigHonkinStruct x)'

 or

 'void foo(in BigHonkinStruct*# x)'

 it would just be:

 'void foo(in BigHonkinStruct x)'

 With that in mind, I *still* say both 'implicit in' and 'explicit in' 
 should be
 changed to act like Walter's proposal because that is what is expected in 
 99% of
 the cases anyhow, and the other 1% is when the programmer forgot the 'out' 
 or
 'inout' with byref vars.

 If the compiler detects even the simple cases, then most of the remaining 
 1%
 wouldn't compile anyway and the programmer would simply have to change it 
 to
 'out' or 'inout' as it should have been in the first place <g>.

 The other issue with using 'inout' as a substitute for '&' is that you 
 can't
 pass a temporary:

 struct S { int x, y, z; }
 S initAnS(int x, int y, int z) { S s; s.x = x, s.y = y, s.z = z; return 
 s; }
 int foo(inout S s) { return s.x * s.y * s.z; }

 void main()
 {
 int i = foo(initAnS(10,20,30)); // Error: initAnS(10,20,30) is not an 
 lvalue
 }

 The optimizations aren't my biggest deal though -- IMHO, the param. 
 storage
 specifiers should mean what they say or imply, it's just that the 
 semantics of
 'in' (both implicit and explicit) need to change to be like the const or
 'readonly' vars. that we've all been talking about and then perhaps (if 
 needed)
 a contrary keyword like 'mutable' could be added.

 Will an 'immutable implicit in' be confusing for C/++ people at first? 
 Perhaps,
 but not after the compiler spits out an error for the first few trivial 
 cases -
 then it will just be "Ok, if I want to modify a function parameter, I have 
 to
 specify 'out' or 'inout' or make a copy.

 Maybe it won't be confusing at all because in C-land they have to use a 
 pointer
 and in C++ land they have to use '&' so maybe making 'implicit in' act 
 like a
 byval for even byref parameters will come more naturally and it's the VB, 
 C# and
 Java people wo will be confused at first.

 I think Walter had it right on his first pass here:

 digitalmars.D/26096

 - Dave

IMHO 'implicit in' forcing immutability would be a big language change from C/C++/Java/C#. Especially if it were easy to break the immutability contract without the compiler throwing fits. I agree if implicit in were changed then a 'mutable' keyword would be needed to avoid having to use inout/out and force lvalues. I also hope it wouldn't encourage people to use global variables in order to avoid passing state around and micro-managing the mutability.
Jul 13 2005
parent Dave <Dave_member pathlink.com> writes:
In article <db3so3$u58$1 digitaldaemon.com>, Ben Hinkle says...
"Dave" <Dave_member pathlink.com> wrote in message 
news:db3pfk$rje$1 digitaldaemon.com...
 In article <db3g4b$jdj$1 digitaldaemon.com>, Andrew Fedoniouk says...
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:db3ejf$i7u$1 digitaldaemon.com...
 A variation on this is to just use the 'final' keyword and forget about
 marking output values. So an input parameter, local variable or field
 marked 'final' means "deep immutable" in Walter's sense for the 
 lifetime
 of that variable. A final variable of array, pointer or reference type
 can be assigned new values but the contents of the array, pointer or
 reference (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 
 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.

Yep, in/out is just byval/byref and has nothing common with constness. BTW: You've provided example which has little sense :) void foo(final inout BigHonkinStruct x) As 'inout' tells us - information will flow in and out but foo cannot modify x so it is only 'in' in fact.

With Walter's 'explicit in' idea, then having 'inout' "double" as a way to pass a byval variable (greater in size than a machine word more efficiently) wouldn't be needed - the compiler could easily decide that and just do the optimization.

It can? How about an example like struct BigHonkinStruct { int a; } //ok not so honkin but you get the idea void foo(in BigHonkinStruct x, void delegate() cb) { int old = x.a; cb(); assert( x.a == old ); } void bar() { BigHonkinStruct y; void baz(){y.a = 10;} foo(y,&baz); } Pass-by-value semantics means that the x inside foo is (semantically) a copy of y so the function bar must be able to change y whenever it wants and it has no impact on x. Hence you can't pass y by reference to foo even though it is declared as 'explicit in'. Are you thinking of something else? I might be mis-reading your point.

What I meant by 'the compiler could easily decide that' is that the compiler could detect whether or not it would be worth it to pass byref or not, not if the compiler could detect 'immutability' cases like that above. I know what pass byval semantics do now <g> Part of what I was getting at is basically that the semantics would have to change -- perhaps get rid of the implicit byval/byref distinction based on what the parameter type is, CT error on the 'easy' cases where 'immutability' is broken for in params and rely on the idea of Walter's 'immutability contract'. It would be one of those areas where D is just different. Continuing the byval/byref though, one of the things that was confusing to people going to Java (where 'everything is an object') is that int 'objects' are passed byval whereas some UDT 'object' is passed byref. So, they ended up modifying an int param in a function and it wouldn't break their code but when they passed a UDT and modified a member of the UDT, it would. This is probably the same with D, expecially for newbies. If you got rid of the implicit byval/byref distinction so people just knew that if you modify 'in' parameter 'x' inside a function it may have side-effects (regardless of the type) then they wouldn't rely on a parameter being byval. This 'mindset' would also help to get rid of hard to find bugs caused by generic programming where the same template functions do something to a type whether or not it resides on the stack or the heap, for example.
 This can be more efficient with something even as small as a long or a 
 double
 prec. fp var. on 32 bit machines.

 so instead of

 'void foo(final inout BigHonkinStruct x)'

 or

 'void foo(in BigHonkinStruct*# x)'

 it would just be:

 'void foo(in BigHonkinStruct x)'

 With that in mind, I *still* say both 'implicit in' and 'explicit in' 
 should be
 changed to act like Walter's proposal because that is what is expected in 
 99% of
 the cases anyhow, and the other 1% is when the programmer forgot the 'out' 
 or
 'inout' with byref vars.

 If the compiler detects even the simple cases, then most of the remaining 
 1%
 wouldn't compile anyway and the programmer would simply have to change it 
 to
 'out' or 'inout' as it should have been in the first place <g>.

 The other issue with using 'inout' as a substitute for '&' is that you 
 can't
 pass a temporary:

 struct S { int x, y, z; }
 S initAnS(int x, int y, int z) { S s; s.x = x, s.y = y, s.z = z; return 
 s; }
 int foo(inout S s) { return s.x * s.y * s.z; }

 void main()
 {
 int i = foo(initAnS(10,20,30)); // Error: initAnS(10,20,30) is not an 
 lvalue
 }

 The optimizations aren't my biggest deal though -- IMHO, the param. 
 storage
 specifiers should mean what they say or imply, it's just that the 
 semantics of
 'in' (both implicit and explicit) need to change to be like the const or
 'readonly' vars. that we've all been talking about and then perhaps (if 
 needed)
 a contrary keyword like 'mutable' could be added.

 Will an 'immutable implicit in' be confusing for C/++ people at first? 
 Perhaps,
 but not after the compiler spits out an error for the first few trivial 
 cases -
 then it will just be "Ok, if I want to modify a function parameter, I have 
 to
 specify 'out' or 'inout' or make a copy.

 Maybe it won't be confusing at all because in C-land they have to use a 
 pointer
 and in C++ land they have to use '&' so maybe making 'implicit in' act 
 like a
 byval for even byref parameters will come more naturally and it's the VB, 
 C# and
 Java people wo will be confused at first.

 I think Walter had it right on his first pass here:

 digitalmars.D/26096

 - Dave

IMHO 'implicit in' forcing immutability would be a big language change from C/C++/Java/C#. Especially if it were easy to break the immutability contract without the compiler throwing fits. I agree if implicit in were changed then a 'mutable' keyword would be needed to avoid having to use inout/out and force lvalues. I also hope it wouldn't encourage people to use global variables in order to avoid passing state around and micro-managing the mutability.

Jul 13 2005
prev sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Dave" <Dave_member pathlink.com> wrote in message 
news:db3pfk$rje$1 digitaldaemon.com...
 In article <db3g4b$jdj$1 digitaldaemon.com>, Andrew Fedoniouk says...
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:db3ejf$i7u$1 digitaldaemon.com...
 A variation on this is to just use the 'final' keyword and forget about
 marking output values. So an input parameter, local variable or field
 marked 'final' means "deep immutable" in Walter's sense for the 
 lifetime
 of that variable. A final variable of array, pointer or reference type
 can be assigned new values but the contents of the array, pointer or
 reference (recursively) cannot change.
 void foo(final char[] str); // Walter's idea with 'final' instead of 
 'in'
 final char[] str = "blah"; // str contents are read-only
 struct Foo { final char[] str; }

It occured to me a benefit of using a keyword other than 'in' is that it allows you to write void foo(final inout BigHonkinStruct x) to get both pass-by-reference and immutability.

Yep, in/out is just byval/byref and has nothing common with constness. BTW: You've provided example which has little sense :) void foo(final inout BigHonkinStruct x) As 'inout' tells us - information will flow in and out but foo cannot modify x so it is only 'in' in fact.

With Walter's 'explicit in' idea, then having 'inout' "double" as a way to pass a byval variable (greater in size than a machine word more efficiently) wouldn't be needed - the compiler could easily decide that and just do the optimization. This can be more efficient with something even as small as a long or a double prec. fp var. on 32 bit machines. so instead of 'void foo(final inout BigHonkinStruct x)' or 'void foo(in BigHonkinStruct*# x)' it would just be: 'void foo(in BigHonkinStruct x)' With that in mind, I *still* say both 'implicit in' and 'explicit in' should be changed to act like Walter's proposal because that is what is expected in 99% of the cases anyhow, and the other 1% is when the programmer forgot the 'out' or 'inout' with byref vars. If the compiler detects even the simple cases, then most of the remaining 1% wouldn't compile anyway and the programmer would simply have to change it to 'out' or 'inout' as it should have been in the first place <g>. The other issue with using 'inout' as a substitute for '&' is that you can't pass a temporary: struct S { int x, y, z; } S initAnS(int x, int y, int z) { S s; s.x = x, s.y = y, s.z = z; return s; } int foo(inout S s) { return s.x * s.y * s.z; } void main() { int i = foo(initAnS(10,20,30)); // Error: initAnS(10,20,30) is not an lvalue } The optimizations aren't my biggest deal though -- IMHO, the param. storage specifiers should mean what they say or imply, it's just that the semantics of 'in' (both implicit and explicit) need to change to be like the const or 'readonly' vars. that we've all been talking about and then perhaps (if needed) a contrary keyword like 'mutable' could be added. Will an 'immutable implicit in' be confusing for C/++ people at first? Perhaps, but not after the compiler spits out an error for the first few trivial cases - then it will just be "Ok, if I want to modify a function parameter, I have to specify 'out' or 'inout' or make a copy. Maybe it won't be confusing at all because in C-land they have to use a pointer and in C++ land they have to use '&' so maybe making 'implicit in' act like a byval for even byref parameters will come more naturally and it's the VB, C# and Java people wo will be confused at first. I think Walter had it right on his first pass here: digitalmars.D/26096 - Dave

Such explicit 'in' needs 'mutable' designators for all methods allowing to change state of structure/class. This is the main concern with 'const' in C++ Again 'in' is a direction of information flow. Please don't mix them with constantess. Through 'in' you can pass both mutable reference and immutable reference. 'in' just means that changes of reference value itself will not be visible for a caller - it is far from "this pointer is only for reading".
Jul 13 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk  
<news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods allowing
 to change state of structure/class. This is the main concern with  
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them with
 constantess.
 Through 'in' you can pass both mutable reference and immutable reference.
 'in' just means that changes of reference value itself will not be  
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.
Jul 13 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz> wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk  
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods allowing
 to change state of structure/class. This is the main concern with  
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them  
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable  
 reference.
 'in' just means that changes of reference value itself will not be  
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) and explicitly copy those variables we need a copy of. Of course in a case like: void foo(int i) { int j = i; } where the compier will pass byvalue (as it's more efficient) and we immediately copy it, making a 2nd copy (no different to how it is today, but), the compiler could optimise this so that the only copy made was 'j', couldn't it? Regan
Jul 13 2005
next sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opstvgxmlz23k2f5 nrage.netwin.co.nz...
 On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz> 
 wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk 
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods allowing
 to change state of structure/class. This is the main concern with 
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them 
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable 
 reference.
 'in' just means that changes of reference value itself will not be 
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) ...

Are we in gambling business or what? Compilation and optimization based on gentlemen's agreements, intentions and brotherhood of men ... I like the abstract idea but may I ask for something more determinstic in this pub?
 ... and explicitly copy those variables we need a copy of.  Of course in a 
 case like:

 void foo(int i) { int j = i; }

 where the compier will pass byvalue (as it's more efficient) and we 
 immediately copy it, making a 2nd copy (no different to how it is today, 
 but), the compiler could optimise this so that the only copy made was 'j', 
 couldn't it?

 Regan 

Jul 13 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 13 Jul 2005 16:08:08 -0700, Andrew Fedoniouk  
<news terrainformatica.com> wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opstvgxmlz23k2f5 nrage.netwin.co.nz...
 On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz>
 wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods  
 allowing
 to change state of structure/class. This is the main concern with
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable
 reference.
 'in' just means that changes of reference value itself will not be
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) ...

Are we in gambling business or what?

No, because the point you're missing is that it doesn't matter at all how it passes it if you only read from it, does it? (ignoring efficiency for a sec, as that is how the compiler will choose to pass it)
 Compilation and optimization based on gentlemen's agreements, intentions  
 and brotherhood of men ...
 I like the abstract idea but may I ask for something more determinstic in
 this pub?

When? Is there any situation where you only want to read from a variable and you must have it passed by value, or by reference? Regan
Jul 13 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opstvj1qwe23k2f5 nrage.netwin.co.nz...
 On Wed, 13 Jul 2005 16:08:08 -0700, Andrew Fedoniouk 
 <news terrainformatica.com> wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opstvgxmlz23k2f5 nrage.netwin.co.nz...
 On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz>
 wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods 
 allowing
 to change state of structure/class. This is the main concern with
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable
 reference.
 'in' just means that changes of reference value itself will not be
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) ...

Are we in gambling business or what?

No, because the point you're missing is that it doesn't matter at all how it passes it if you only read from it, does it? (ignoring efficiency for a sec, as that is how the compiler will choose to pass it)

Oh, no, please... this horse is dead already... "1. ... if passed byref it would have to enforce read only." How the hell compiler will enforce it? This is the whole point. Check can be made in runtime by running GC-like cycle against this variable. This is the only non-intrusive way as far as I know. Do you like it? Andrew.
 Compilation and optimization based on gentlemen's agreements, intentions 
 and brotherhood of men ...
 I like the abstract idea but may I ask for something more determinstic in
 this pub?

When? Is there any situation where you only want to read from a variable and you must have it passed by value, or by reference? Regan

Jul 13 2005
parent reply Dave <Dave_member pathlink.com> writes:
In article <db4umv$1spa$1 digitaldaemon.com>, Andrew Fedoniouk says...
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opstvj1qwe23k2f5 nrage.netwin.co.nz...
 On Wed, 13 Jul 2005 16:08:08 -0700, Andrew Fedoniouk 
 <news terrainformatica.com> wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opstvgxmlz23k2f5 nrage.netwin.co.nz...
 On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz>
 wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods 
 allowing
 to change state of structure/class. This is the main concern with
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable
 reference.
 'in' just means that changes of reference value itself will not be
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) ...

Are we in gambling business or what?

No, because the point you're missing is that it doesn't matter at all how it passes it if you only read from it, does it? (ignoring efficiency for a sec, as that is how the compiler will choose to pass it)

Oh, no, please... this horse is dead already... "1. ... if passed byref it would have to enforce read only." How the hell compiler will enforce it? This is the whole point.

Basically, what I (and I think Regan) have in mind is that the compiler would enforce 'in' params. the same way a C++ compiler would enforce "const [type]" or "const [type] &" params. for analogous types in C++. That way you would get 'C++ const' functionality for params by default (implicit in params would act like Walter's 'explicit in' proposal too). The difference would be that in D, although it wouldn't be enforced "deeply" by the compiler (because as you've pointed out many times that isn't practical), implicit and explicit 'in' would carry a 'gentlemen's agreement' that these are immutable so that 'in' could have semantic value. I think that a pretty high majority of the "oops" cases would be caught by the "const [type] &" checks analogous to what a good C++ compiler does and then the programmer would have to change the param to out or inout or explicitly make a copy. For the other cases it would be undefined behaviour because the 'contract' is being broken. The reasons this makes sense to me are this: - It would cover most cases and therefore eliminate peppering function declarations with type/storage modifiers. - It wouldn't add anything to D syntax. - It would provide (and enforce) the same C++ const type functionality that it seems a lot of people in this discussion have been referring to. - It would seperate how a parameter is passed (byref or byval) from what type it is (struct, class, array, primitive, pointer) so a D programmer could stop thinking about that and the compiler could do what makes the most sense when passing vars. w/o the programmer having to tack '&' onto every param. - Since 'implicit in' parameters are probably and will stay a good majority, the compiler could take advantage of the contract to produce better code inside the function for expressions involving the 'in' params. - It would reinforce the idea that the 'direction' of a param. should coincide with whether or not it is modified inside a function. For example, when passing an instance of a class 'in', you can now (legally) directly modify primitive members of that class and those modifications live past the lifetime of that function call. However, passing an instance of a struct modifying the member won't in many cases, which just seems contradictory to me: class C { int i = 10; } struct S { int i = 10; } template foo(T) { void foo(in T o) { o.i = 20; } } void main() { C c = new C; printf("%d\n",c.i); foo!(C)(c); printf("%d\n",c.i); S s; printf("%d\n",s.i); foo!(S)(s); printf("%d\n",s.i); } One of Walter's goals is to make the compiler much easier to implement than C++, and I'm not sure if doing const param. checks would be one of the things that would severly impact achieving that goal. It *would* require a new mindset, but I think now's the time to try it out. It would be interesting to add (just) the C++-like const checks to the next build and have everyone run their code through it to see how much code it breaks (as in won't compile) and then regress if it causes too many problems. If it isn't too much effort for Walter, I think it'd be worth a try - this may well 'break' less code than changing AA's (as in the other current discussion) would.
Check can be made in runtime by running GC-like cycle against this variable.
This is the only non-intrusive way as far as I know.  Do you like it?

Andrew.

 Compilation and optimization based on gentlemen's agreements, intentions 
 and brotherhood of men ...
 I like the abstract idea but may I ask for something more determinstic in
 this pub?

When? Is there any situation where you only want to read from a variable and you must have it passed by value, or by reference? Regan


Jul 14 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 14 Jul 2005 16:10:05 +0000 (UTC), Dave <Dave_member pathlink.com>  
wrote:
 In article <db4umv$1spa$1 digitaldaemon.com>, Andrew Fedoniouk says...
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opstvj1qwe23k2f5 nrage.netwin.co.nz...
 On Wed, 13 Jul 2005 16:08:08 -0700, Andrew Fedoniouk
 <news terrainformatica.com> wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opstvgxmlz23k2f5 nrage.netwin.co.nz...
 On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz>
 wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods
 allowing
 to change state of structure/class. This is the main concern with
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix  
 them
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable
 reference.
 'in' just means that changes of reference value itself will not be
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) ...

Are we in gambling business or what?

No, because the point you're missing is that it doesn't matter at all how it passes it if you only read from it, does it? (ignoring efficiency for a sec, as that is how the compiler will choose to pass it)

Oh, no, please... this horse is dead already... "1. ... if passed byref it would have to enforce read only." How the hell compiler will enforce it? This is the whole point.

Basically, what I (and I think Regan) have in mind is that the compiler would enforce 'in' params. the same way a C++ compiler would enforce "const [type]" or "const [type] &" params. for analogous types in C++. That way you would get 'C++ const' functionality for params by default (implicit in params would act like Walter's 'explicit in' proposal too). The difference would be that in D, although it wouldn't be enforced "deeply" by the compiler (because as you've pointed out many times that isn't practical), implicit and explicit 'in' would carry a 'gentlemen's agreement' that these are immutable so that 'in' could have semantic value. I think that a pretty high majority of the "oops" cases would be caught by the "const [type] &" checks analogous to what a good C++ compiler does and then the programmer would have to change the param to out or inout or explicitly make a copy. For the other cases it would be undefined behaviour because the 'contract' is being broken. The reasons this makes sense to me are this: - It would cover most cases and therefore eliminate peppering function declarations with type/storage modifiers. - It wouldn't add anything to D syntax. - It would provide (and enforce) the same C++ const type functionality that it seems a lot of people in this discussion have been referring to. - It would seperate how a parameter is passed (byref or byval) from what type it is (struct, class, array, primitive, pointer) so a D programmer could stop thinking about that and the compiler could do what makes the most sense when passing vars. w/o the programmer having to tack '&' onto every param. - Since 'implicit in' parameters are probably and will stay a good majority, the compiler could take advantage of the contract to produce better code inside the function for expressions involving the 'in' params. - It would reinforce the idea that the 'direction' of a param. should coincide with whether or not it is modified inside a function. For example, when passing an instance of a class 'in', you can now (legally) directly modify primitive members of that class and those modifications live past the lifetime of that function call. However, passing an instance of a struct modifying the member won't in many cases, which just seems contradictory to me: class C { int i = 10; } struct S { int i = 10; } template foo(T) { void foo(in T o) { o.i = 20; } } void main() { C c = new C; printf("%d\n",c.i); foo!(C)(c); printf("%d\n",c.i); S s; printf("%d\n",s.i); foo!(S)(s); printf("%d\n",s.i); } One of Walter's goals is to make the compiler much easier to implement than C++, and I'm not sure if doing const param. checks would be one of the things that would severly impact achieving that goal. It *would* require a new mindset, but I think now's the time to try it out. It would be interesting to add (just) the C++-like const checks to the next build and have everyone run their code through it to see how much code it breaks (as in won't compile) and then regress if it causes too many problems. If it isn't too much effort for Walter, I think it'd be worth a try - this may well 'break' less code than changing AA's (as in the other current discussion) would.

Exactly! Regan
Jul 14 2005
prev sibling parent Dave <Dave_member pathlink.com> writes:
In article <opstvgxmlz23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Thu, 14 Jul 2005 09:56:02 +1200, Regan Heath <regan netwin.co.nz> wrote:
 On Wed, 13 Jul 2005 13:29:24 -0700, Andrew Fedoniouk  
 <news terrainformatica.com> wrote:
 Such explicit 'in' needs 'mutable' designators for all methods allowing
 to change state of structure/class. This is the main concern with  
 'const' in C++

 Again 'in' is a direction of information flow. Please don't mix them  
 with
 constantess.
 Through 'in' you can pass both mutable reference and immutable  
 reference.
 'in' just means that changes of reference value itself will not be  
 visible
 for a caller - it is far from "this pointer is only for reading".

There are several ways you might want to pass a parameter: 1. read only. the mechanism of passing copy/byref isn't important you just want a variable you can only read from. the compiler could essentially decide to pass this in any way it likes. if passed byref it would have to enforce read only. 2. copy. you want a copy that you can modify inside the function for the duration of the function. 3. refrence. you want the actual variable so that you can modify it inside the function. Right now D has 2 and 3 covered but not 1. The OP is suggesting something that makes #1 possible, and changes #2 so it is only possible if the programmer codes it explicitly. #3 remains the same.

Sorry, by OP I meant "Dave". I realise in, out, and inout currently specify how to pass a variable, but, in they also allow the programmer to declare how they intend to use the variable. So while technically they do not declare constness or similar they imply it in cases. I can't really see a point in ever passing byval, we can instead pass byX (compilers choice) and explicitly copy those variables we need a copy of.

Exactly..
Of course in a case like:

void foo(int i) { int j = i; }

where the compier will pass byvalue (as it's more efficient) and we  
immediately copy it, making a 2nd copy (no different to how it is today,  
but), the compiler could optimise this so that the only copy made was 'j',  
couldn't it?

IMHO, it wouldn't matter in a good majority of cases where an int (or a long on 64 bit machines) is passed 'in' anyway because when a simple var. is passed in like that it is usually only part of the rvalue for expressions like: int x = i * 10; and not modified (at least in my experience) so the programmer wouldn't usually feel compelled to make a modifiable copy anyhow. Regardless, I just ran a quick test with the dmd compiler. The optimization you speak of is apparently already done (dead assignment elimination, I think). Compiled w/ either '-version=nocopy' or not (and -O -inline -release) this actually results in the exact same assembly code: #import std.stdio; #void main() #{ # int sum = 0; # for(int i = 0, j = 0; i < 1000000000; i++, j--) # { # sum += foo(i,j); # } # writefln(sum); #} #int foo(int x, int y) #{ # version(nocopy) # { # return x++ + y--; # } # else # { # int x2 = x, y2 = y; # return x2++ + y2--; # } #}
Regan

Jul 13 2005
prev sibling parent "Regan Heath" <regan netwin.co.nz> writes:
The idea I have been posting for the last few weeks...

 1) Reasonable model of "costness"

'readonly' type modifier (*) 'in' (implicit/explicit) parameters are 'readonly' (*) note, this type modifier does *not* create a distinct type, instead it flags a variable as being readonly. More on the flag below. This is the essential and important difference between this and C++ const.
 2) Notation. E.g. how readonly slice will look like
      in code. Function parameters, etc.

readonly char[] foo() {} //returns a readonly char[] p = foo(); //p is readonly (*) char[] s = p; //s is readonly (*) void bar(char[] p) {} //p is readonly void bar(in char[] p) {} //p is readonly (*) note, the type modifier is not required on variable declarations, instead they become readonly by assignment from a readonly RHS. "readonly char[]" is not a distinct type, it is simply "char[]" with a readonly *compile time* flag set). It's important to note that this readonly cannot be cast away like C++ const, the only way to get a mutable version of a readonly variable is to use dup, eg. void foo(char[] p) //p is readonly { char[] s = p.dup; s[0] = 'a'; ... } This essentially induces correct COW behaviour.
 3) How to avoid "clutter". This term appears here
      not once - means that it is reasonable - so
      we need to deal with it.

The common cases require no notation, eg. parameters - read only by default. return values - writable by default. The remaining 'clutter' isn't clutter IMO but a required declaration of the programmers intent. i.e. void bar(out char[] p) {} //I will write to 'p' void bar(inout char[] p) {} //I will read and write to 'p' readonly char[] p = "test"; //p is readonly readonly char[] foo() {} //return is readonly 4) Suggested implementation A large percentage of readonly violations can be detected at compile time by simply flagging variables during the compile phase, passing that flag on during assignment and giving an error when violations are detected. For certain cases eg. char[] s = condition?foo():""; s[0] = 'a'; //? readonly, or not The only soln I can imagine is a runtime readonly flag. This might be too much cost for very little additional gain. It could however be enabled/disabled much like unittest or other dbc features are. In some cases i.e. functions with 'in' parameters runtime protection/detection can be achieved with the following DBC style code which copies and then compares the readonly variables ensuring no violation has occurred. eg. void foo(char[] p) in { copy = malloc(p.sizeof); memcpy(copy,&p,p.sizeof); } out() { assert(memcmp(copy,&p,p.sizeof) == 0); } body { //causes violation p.length = 20; } It's possible this sort of thing could be applied to other scope entrace/exit points. Regan
Jul 05 2005