July 01, 2005
Hi,

>One of my (several) dislikes with "const" is that most (90% ?) of a function's parameters are likely to be "const". So one winds up with the C++ aesthetically ugly consequence of "const" here, there, everywhere. And you can't use it casually here and there, once you start down the road of making a few functions have "const" parameters, you've got to do it throughout the program.

Very true.

>One thing I have thought of is a compromise. Change the semantics of explicitly "in" parameters to be what Andrei calls "deep immutable". 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.

This would be fantastic, IMHO. It makes perfect sense, and we get the best of both worlds. [a] Immutable variables exactly where we need them; and [b] the simple aesthetic syntax. This should have always been the default, and it definitely gets my vote by far.

Cheers,
--AJG.




=======================
I sync, therefore I am.
July 01, 2005
In article <da2v7t$b9b$1@digitaldaemon.com>, Walter says...
>One thing I have thought of is a compromise. Change the semantics of explicitly "in" parameters to be what Andrei calls "deep immutable". 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. The values wouldn't change even by another reference or another thread. (This is quite unlike C++ "const".) "in" would be a promise by the programmer, and could not be guaranteed by the compiler - but - the optimizer can take advantage of this promise to generate perhaps significantly better code. (With C++ "const", values can change at any momen t by another reference or another thread, making optimizations based on "const" impossible.)

This sounds great.  In this scenario, can I store the in object (or its sub objects) to a local variable?  If so it would be immutable too I would guess, but is there a special syntax?  (Or is slicing, etc forbidden in these cases?)

:void foo(in int[] x)
:{
:    int[] middle1 = x[1..$-1];    // is middle implicitely immutable?
:    in int[] middle2 = x[1..$-1]; // explicitly marked as immutable?
:    middle[0] = middle1[1];       // this should fail to compile right?
:}

>C++ "const" is enforced by the compiler, but is useless semantic information. "in" is not enforced by the compiler beyond the trivial, but is useful semantic information.

Kevin


July 01, 2005
"Kevin Bealer" <Kevin_member@pathlink.com> wrote in message news:da3ppb$1h97$1@digitaldaemon.com...
> This sounds great.  In this scenario, can I store the in object (or its
sub
> objects) to a local variable?  If so it would be immutable too I would
guess,
> but is there a special syntax?  (Or is slicing, etc forbidden in these
cases?)
>
> :void foo(in int[] x)
> :{
> :    int[] middle1 = x[1..$-1];    // is middle implicitely immutable?

I don't see how to make that work right. The only thing I can think of is to allow middle1 to modify x, but that such modification would be undefined behavior. Not so hot.

> :    in int[] middle2 = x[1..$-1]; // explicitly marked as immutable?

There I hate using 'in' as a storage class. It's confusing with 'const'.

> :    middle[0] = middle1[1];       // this should fail to compile right?

Yes. That's one case the compiler should pick up.



July 01, 2005
In article <da3ug1$1n4l$1@digitaldaemon.com>, Walter says...
>
>
>"Kevin Bealer" <Kevin_member@pathlink.com> wrote in message news:da3ppb$1h97$1@digitaldaemon.com...
>> This sounds great.  In this scenario, can I store the in object (or its
>sub
>> objects) to a local variable?  If so it would be immutable too I would
>guess,
>> but is there a special syntax?  (Or is slicing, etc forbidden in these
>cases?)
>>
>> :void foo(in int[] x)
>> :{
>> :    int[] middle1 = x[1..$-1];    // is middle implicitely immutable?
>
>I don't see how to make that work right. The only thing I can think of is to allow middle1 to modify x, but that such modification would be undefined behavior. Not so hot.

I think in this case a copy would need to be made.  ie.

int[] middle1 = x[1..$-1].dup;

This is no different than when a programmer wants to modify a const parameter in C++.  I'm not sure that there's a good way to enforce this in the compiler, though it might be nice if there were a way to verify that a parameter is unchanged via DBC.  Perhaps something like this?

void fn( in int[] x )
in {
preserve int[] p = x.dup;
}
out {
assert( x == p );
}

or perhaps:

void fn( in int[] x )
int[] p;
in {
p = x.dup;
}
..


Sean


July 01, 2005
Walter escribió:
> 
> One thing I have thought of is a compromise. Change the semantics of
> explicitly "in" parameters to be what Andrei calls "deep immutable". 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. The values wouldn't change even by another reference or
> another thread. (This is quite unlike C++ "const".) "in" would be a promise
> by the programmer, and could not be guaranteed by the compiler - but - the
> optimizer can take advantage of this promise to generate perhaps
> significantly better code. (With C++ "const", values can change at any momen
> t by another reference or another thread, making optimizations based on
> "const" impossible.)
> 

I think that's similar to what Pascal does, and that's something I agree with.

One point that isn't addressed yet (I think) is returning something: can it be modified? What happens if it's modified? etc.

-- 
Carlos Santander Bernal
July 01, 2005
If he does, then why can't all 'in' parameters be immutable. 'in' is the default parameter type, so all variables would be immutable by default. No 'const' (or whatever keyword is used) everywhere. I've always thought 'in' was part of the function contract stating "I only need read access to this", this change would actually enforce that.

If a mutable copy was required then:

void foo(char[] a)
{
  char[] b = a.dup;
}

would work. It could even be optimised by the compiler. I imagine the compiler is already making a dup of the variable which is passed (as it does for value types), this would make a second one, it could optimise one of those duplicates away.

Regan

On Fri, 1 Jul 2005 13:20:18 +0000 (UTC), AJG <AJG_member@pathlink.com> wrote:
> I think he means "b". As opposed to parameter in variables, like "a".
> --AJG.
>
> In article <opss8aohii23k2f5@nrage.netwin.co.nz>, Regan Heath says...
>>
>> On Fri, 1 Jul 2005 01:34:31 -0700, Walter <newshound@digitalmars.com>
>> wrote:
>>> But that runs into another problem. Function local variables, on the
>>> other hand, one would want to be "mutable" by default.
>>
>> By function local do you mean "a" or "b" below?
>>
>> void foo(char[] a)
>> {
>>   char[] b;
>> }
>>
>> Regan
>
>

July 01, 2005
I recently forgot my 'other' option for runtime 'readonly' checking of "in" parameters, this solution uses in/out contracts, eg.

import std.stdio;
import std.string;
import std.c.stdlib;

void main()
{
	char[] a = "regan";	
	foo(a);
}

void* cmp = null;

void foo(char[] string)
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;
}



As you can see it simply makes a duplicate of the 'in' parameter then compares the parameter with the duplicate upon completion of the function.

This is better than a readonly flag in every object as it saves memory overall. It is worse in that it doesn't tell you exactly where it was modified, just that it was modified somewhere in that function.

This idea doesn't necessarily help us with "const" labelled returns eg.

class Foo {
  private char[] data;
  const char[] get() { return data[0..5]; }
}

char[] p = f.get();
char[] s = b ? p : "foo"; //where b is determined at runtime.
s[0] = 'a';

However a copy could be setup when f.get() returned and checked when 'p' went out of scope. This sounds difficult ot implement to me however.

Regan

On Fri, 01 Jul 2005 19:46:23 +1200, Regan Heath <regan@netwin.co.nz> wrote:
> On Fri, 1 Jul 2005 00:35:38 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote:
>>>>> 1 bit in every single variable.
>>>>
>>>> The question is: where this bit is located?
>>>> array variable is 32bit ptr (64bit) and 32bit (64bit) length.
>>>> pointer variable is 32bit (64bit). So where is the place for it?
>>>> In additional machine word which will be assosiated with
>>>> each variable?
>>>
>>> If that is what is required, yes.
>>
>> So -release will have sizeof(void*) == 32
>> and otherwise sizeof(void*) == 64.
>>
>> Do you *really* think this is a solution?
>
> Yes.
>
> Almost all cases are handled at *compile* time, there are very few cases which cannot be handled at compile time.
>
> As such the runtime solution is a very small piece of the overall solution. It's optional, can be disabled by a switch, and would *only* be used during the design stage just like the other DBC features.
>
>>>>>> Proposed solution is just static (compile time)  test.
>>>>>
>>>>> Mine is first a compile time suggestion, followed by a runtime one (as
>>>>> it's been shown that some cases exist where compile time check is
>>>>> impossible)
>>>>>
>>>>>> Moving conditions which can be checked in compile to runtime
>>>>>> is generally not a good idea - non-efficient and difficult
>>>>>> to verify in full.
>>>>>
>>>>> Like I said above, my primary suggestion is a compile time one, it has
>>>>> been shown not all cases can be verified at compile time, as such a
>>>>> runtime solution is required.
>>>>
>>>> Universal solution is to have opAssign, etc.
>>>
>>> That doesn't give us immutable char[].
>>
>> It will give you an option:
>>
>> struct chars {
>>    private char[] data;
>>    bit mutableBit;
>>
>>    void opAssign(chars rightSide)
>>    {
>>        if(!mutableBit)
>>           throw new WellKnownAndExpectedExceptionAndNotAnError;
>>        ...
>>    }
>>
>> }
>
> D does not need a string class.
>
> Regan

July 02, 2005
"Carlos Santander" <csantander619@gmail.com> wrote in message news:da4e0i$27v9$1@digitaldaemon.com...
> One point that isn't addressed yet (I think) is returning something: can it be modified? What happens if it's modified? etc.

With the 'in' compromise, it doesn't address return values.


July 02, 2005
"Walter" <newshound@digitalmars.com> wrote in message news:da2v7t$b9b$1@digitaldaemon.com...
>
> "Andrew Fedoniouk" <news@terrainformatica.com> wrote in message news:da024r$65o$1@digitaldaemon.com...
>> I would like to discuss one of my previous ideas again
>> as it seems it was lost in the fire.
>
> So far, I haven't thought of anything that makes consistent sense, and doesn't look like an ugly hack.
>
> One thing I have thought of is a compromise. Change the semantics of
> explicitly "in" parameters to be what Andrei calls "deep immutable". 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. The values wouldn't change even by another reference or
> another thread. (This is quite unlike C++ "const".) "in" would be a
> promise
> by the programmer, and could not be guaranteed by the compiler - but - the
> optimizer can take advantage of this promise to generate perhaps
> significantly better code. (With C++ "const", values can change at any
> momen
> t by another reference or another thread, making optimizations based on
> "const" impossible.)
>

One other thought - Make it so that an "explicit in" makes a copy as it does now, and the default (non-specified) param. storage class is "deep immutable".

Pointer params would act the same as they do now regardless, and functions with extern(...) would be exempt as well.

If pointers were taken out of the picture, why couldn't the compiler catch cases like below?

class C { ... }

void foo(C c, int[] arr)
{
    // Errors
    c = new C;
    c.i = 10;
    int* z = &c.i;
    bar(c);
    C x = c;
    arr = new int[20];
    arr[10] = 10;
    arr[] = 0;
    int[] b = arr;
    int[] b4 = arr[0..10];
    int* y = &a[5];

   // Ok
    int[] b2 = arr.dup;
    int[] b3 = arr[];
    int[] b5 = arr[0..10].dup;
    int* p = cast(int*)&a[6];
    void* v = cast(void*)&a[6];
    *(cast(int*)v) = 700;
    baz(c);
}

void bar(inout C x) { ... }
void baz(C x) { ... }

In all those cases either the param is directly an lvalue /or/ the address of the param is directly taken, or the same for a member of an aggregate.

The spec. would explicitly say "the compiler is responsible for checking /only/ for cases where "either the param (or one of it's aggragate members) is directly assigned to, the address of the param (or one of its aggragate members) is directly taken or the param (or an aggregate member) is directly passed into a function via an out or inout parameter. All other cases are undefined."


July 04, 2005
"Walter" <newshound@digitalmars.com> wrote in message news:da2v7t$b9b$1@digitaldaemon.com...
>
> "Andrew Fedoniouk" <news@terrainformatica.com> wrote in message news:da024r$65o$1@digitaldaemon.com...
>> I would like to discuss one of my previous ideas again
>> as it seems it was lost in the fire.
>
> I've been discussing this issue with Andrei (of Modern C++ Design fame).
> He
> points out that there are numerous different "const" semantics, each
> having
> their uses and disadvantages. He suggests that all should be supported in
> one way or another, and I agree that that would have some advantages.
>
> One of my (several) dislikes with "const" is that most (90% ?) of a
> function's parameters are likely to be "const". So one winds up with the
> C++
> aesthetically ugly consequence of "const" here, there, everywhere. And you
> can't use it casually here and there, once you start down the road of
> making
> a few functions have "const" parameters, you've got to do it throughout
> the
> program.
>
> So perhaps one could turn that inside out, and make "const" the default
> for
> function parameters. If the parameter was to be changed, it'd be
> explicitly
> marked as "mutable" or some such.
>
> But that runs into another problem. Function local variables, on the other
> hand, one would want to be "mutable" by default. There's also the issue
> that
> "const" already exists as a variable storage class.
>

I guess I don't understand why this is a problem because there has always been a 'seperation' and distinction between locals and params to me. I realize there is a union of scope inside the function and they share many of the same syntax and semantic rules as variables, but I've always considered them two seperate and distinct groups of vars. I think most non-newbie programmers realize this and recognize that any params passed by reference also 'live' outside of the function.

Why do you think a typical programmer would be completely put off by the idea that an 'implicit in' param couldn't be modified, while by default a local variable could (especially if the compiler would constantly reinforce the idea that "hey - D's default params are different" by reporting trivial-case errors)?

Thanks,

- Dave