Thread overview
'var' and 'volatile' as !const, with invariant-by-default
Jun 25, 2007
Russell Lewis
Jun 25, 2007
BCS
Jun 25, 2007
Russell Lewis
Jun 25, 2007
BCS
Jun 25, 2007
Russell Lewis
Jun 26, 2007
David B. Held
Jun 26, 2007
Russell Lewis
June 25, 2007
So I understand what Walter is going for with the whole const/invariant/final thing, but it seems that the syntax is causing a lot of confusion.  (I know it isn't clear to me!)  People have been arguing back and forth about const-by-default, and here's my version:

* Invarant-by-default.  That is, an unadorned variable (whether it is
  a global variable, function local, or parameter) is always invariant.
* Use the keyword 'var' to represent a variable which can be modified.
* Use the keyword 'volatile' to indicate that there are aliases of the
  variable, so it is not safe to cache the value of the variable in a
  register.
* 'volatile' is illegal on value-types.  (Only allowed on pointers,
  arrays, class references, etc.)

EXAMPLES:

             uint myCompileTimeConstant = 10;
             uint my_final_equivalentVariable = DoRuntimeCalculation();
         var uint  myOrdinaryVariable;
         var uint *theOnlyPointerToSomething;
volatile var uint *oneOfManyAliases;
volatile     uint *readonlyAlias;

TAKING POINTERS:

The type of the pointer depends on the type of the original variable:

* If the original variable has no modifiers, then the pointer has no
  modifiers as well.  (Totally readonly)
* If the original variable has 'var', then the pointer has 'volatile'
  (the pointer is an alias, and the pointed-to thing might change, but
  the pointer must not be used to change the variable, since the
  original is NOT marked 'volatile')
* If the original variable has 'volatile', then the pointer has
  'volatile'.  This means that there may exist some 3rd alias to the
  underlying variable which could change it.  See note below.
* If the original variable has 'volatile var', then the pointer does as
  well.

NOTE: When taking a pointer to 'volatile', you might want to make the pointer 'volatile var', on the idea that the pointer might be the way that the original variable is modified.  However, 'volatile' is most useful as a way to pass a reference into a library, and we don't want the library to accidentally start modifying what was supposed to be readonly data.  To turn on 'var' ALWAYS requires an explicit cast.

IMPLICIT CASTS:

* You can implicitly cast *away* 'var'.  To add it in always requires
  an explicit cast.
* You can implicitly *add* 'volatile'.  To remove it always requires
  an explicit cast.

EXAMPLES WITH FUNCTION CALLS:

uint myFunc(uint a,char[] b, myStruct *c);
uint myVolatileAwareFunc(uint a,volatile char[] b,volatile myStruct *c);

var uint        globalUint;
var char[]      globalArray;
var myStruct    globalStruct;
    char[]   ro_globalArray;
    myStruct ro_globalStruct;

ILLEGAL (pointers have 'volatile' tag, parameters don't):
  myFunc(globalUint, &globalArray, &globalStruct);
LEGAL (parameters have correct tags):
  myVolatileAwareFunc(globalUint, &globalArray, &globalStruct);
  myFunc(globalUint, &ro_globalArray, &ro_globalStruct);

NOTE: The first argument in the last call above is legal because uints are passed by value, not reference, so the parameter is *not* volatile.



Thoughts?  I know, to implement this would require a complete rewrite of existing D code...but let's ignore that for a moment and just as "is it better?"  If so, then we can ask "is it worth the cost of a rewrite?"

Russ
June 25, 2007
Reply to Russell,

> So I understand what Walter is going for with the whole
> const/invariant/final thing, but it seems that the syntax is causing a
> lot of confusion.  (I know it isn't clear to me!)  People have been
> arguing back and forth about const-by-default, and here's my version:
> 
> * Invarant-by-default.  That is, an unadorned variable (whether it is
> a global variable, function local, or parameter) is always
> invariant.
> * Use the keyword 'var' to represent a variable which can be modified.
> * Use the keyword 'volatile' to indicate that there are aliases of the
> variable, so it is not safe to cache the value of the variable in a
> register.
> * 'volatile' is illegal on value-types.  (Only allowed on pointers,
> arrays, class references, etc.)

unless all var value type are volatile this has a problem.

var int i = 3;
var volatile int* j = &i;

i=5; if(*j)... // ok j volatile

*j = 3; if(i)... // oops i is aliased

how about allow volatile on value types, and requiter it to get a non read only reference to it

var int i = 3;
var volatile int j = 3;

auto ip = &i; // read only reference to i.
auto jp = &j; // read/write reference to j.

auto jp = ip; // invalid


> Thoughts?
> 
> Russ
> 


June 25, 2007
BCS wrote:
> Reply to Russell,
> 
>> So I understand what Walter is going for with the whole
>> const/invariant/final thing, but it seems that the syntax is causing a
>> lot of confusion.  (I know it isn't clear to me!)  People have been
>> arguing back and forth about const-by-default, and here's my version:
>>
>> * Invarant-by-default.  That is, an unadorned variable (whether it is
>> a global variable, function local, or parameter) is always
>> invariant.
>> * Use the keyword 'var' to represent a variable which can be modified.
>> * Use the keyword 'volatile' to indicate that there are aliases of the
>> variable, so it is not safe to cache the value of the variable in a
>> register.
>> * 'volatile' is illegal on value-types.  (Only allowed on pointers,
>> arrays, class references, etc.)
> 
> unless all var value type are volatile this has a problem.
> 
> var int i = 3;
> var volatile int* j = &i;

This line is not legal, in my original rules, because when you take a pointer to a 'var' variable, you get a 'volatile' pointer.  You can't assign that to a 'volatile var' variable without an explicit cast.

I'm open to the idea that you could allow 'volatile' on value-types, but I'm skeptical as to its value.  But don't let my skepticism shoot down the whole idea. :)

As for your example with the variables 'j' and 'jp' below, that would be exactly how my proposal would work (if 'volatile' were allowed on value-types).  In my original post, I said that if you take a pointer to a 'volatile var' you get a 'volatile var'.

So you & I are on the same wavelength. :)

> i=5; if(*j)... // ok j volatile
> 
> *j = 3; if(i)... // oops i is aliased
> 
> how about allow volatile on value types, and requiter it to get a non read only reference to it
> 
> var int i = 3;
> var volatile int j = 3;
> 
> auto ip = &i; // read only reference to i.
> auto jp = &j; // read/write reference to j.
> 
> auto jp = ip; // invalid
> 
> 
>> Thoughts?
>>
>> Russ
>>
> 
> 
June 25, 2007
Reply to Russell,

> BCS wrote:
> 
>> unless all var value type are volatile this has a problem.
>> 
>> var int i = 3;
>> var volatile int* j = &i;
>
> This line is not legal, in my original rules, because when you take a
> pointer to a 'var' variable, you get a 'volatile' pointer.  You can't
> assign that to a 'volatile var' variable without an explicit cast.
>

So the only way to get a non read only reference is with a cast?

About half the time I use references, it it to get write access. Requiring an cast every time would be a major pain.

I'd make the '&' as permissive as possible and have the assignment to a non var pointer drop wright access. This sort of going with the idea that code should assert what it will and wont do, but the compiler shouldn't restrict it beyond that.

Hm. Here their be Interesting thoughts. ;)

> As for your example with the variables 'j' and 'jp' below, that would
> be exactly how my proposal would work (if 'volatile' were allowed on
> value-types).  In my original post, I said that if you take a pointer
> to a 'volatile var' you get a 'volatile var'.
> 
> So you & I are on the same wavelength. :)
> 
>> i=5; if(*j)... // ok j volatile
>> 
>> *j = 3; if(i)... // oops i is aliased
>> 
>> how about allow volatile on value types, and requiter it to get a non
>> read only reference to it
>> 
>> var int i = 3;
>> var volatile int j = 3;
>> auto ip = &i; // read only reference to i.
>> auto jp = &j; // read/write reference to j.
>> auto jp = ip; // invalid
>> 


June 25, 2007
BCS wrote:
> Reply to Russell,
> 
>> BCS wrote:
>>
>>> unless all var value type are volatile this has a problem.
>>>
>>> var int i = 3;
>>> var volatile int* j = &i;
>>
>> This line is not legal, in my original rules, because when you take a
>> pointer to a 'var' variable, you get a 'volatile' pointer.  You can't
>> assign that to a 'volatile var' variable without an explicit cast.
> 
> So the only way to get a non read only reference is with a cast?
> 
> About half the time I use references, it it to get write access. Requiring an cast every time would be a major pain.
> 
> I'd make the '&' as permissive as possible and have the assignment to a non var pointer drop wright access. This sort of going with the idea that code should assert what it will and wont do, but the compiler shouldn't restrict it beyond that.
> 
> Hm. Here their be Interesting thoughts. ;)

Good thoughts.  You can certainly declare the original variable as 'volatile var', which means that the reference would allow writing. However, it would mean that you couldn't cache the value of the variable  either in the caller or the callee.

For the case that you are describing (where a caller passes a pointer to a non-volatile variable to a callee, which presumably won't use it outside the scope of the call), we need a way for the two components to agree that:
a) There won't be another thread in the machine, modifying the variable while the callee runs
b) The callee won't keep a copy of the reference afterwards.

If we can syntacically enforce both of the above, then both the caller and callee can cache the variable (except that the caller must discard his copy exactly once-when he calls the function).  I'm still pondering how you might do that.
June 26, 2007
Russell Lewis wrote:
> [...]
> * Invarant-by-default.  That is, an unadorned variable (whether it is
>   a global variable, function local, or parameter) is always invariant.
> * Use the keyword 'var' to represent a variable which can be modified.
> * Use the keyword 'volatile' to indicate that there are aliases of the
>   variable, so it is not safe to cache the value of the variable in a
>   register.
> * 'volatile' is illegal on value-types.  (Only allowed on pointers,
>   arrays, class references, etc.)
> [...]

I think a *lot* of programmers would find it onerous to decorate all mutable identifiers.  I think pure value-style programming is best enabled by a fully supported functional environment, which D does not quite have.  It's still fairly awkward to define lambads and currying is not supported to the extent that it is in mainstream FP langs.  Part of D's elegance is that it is not overly verbose, like Java or Ada or other languages.  I also think that invariant-by-default would totally surprise people who were told that D is an "imperative" language that is "a lot like C++".  Remember that a language is as much a cultural phenomenon as a technical one.

Dave
June 26, 2007
I totally agree, strangely enough.  The syntax I proposed is what (IMHO) would work well for a large, formal project, but would be stifling for small prototype programs.  I've been pondering if there is some way to switch the behavior on and off at will.

How about an attribute which would turn on the invariant-by-default functionality?

  uint myVar;  // readonly, since there is no attribute

  invariant-by-default bool myFunc() { ... } // only this func

  uint anotherNormalVar;

  invariant-by-default: // all declarations after this are affected
      uint myROvar;
  var uint myRWvar;

It's a little ugly, but it allows for quick prototype programs to use the old syntax, while large projects could just put 'invariant-by-default' at the top of all of their source files to get the nice documentation & optimizing features.

Russ

David B. Held wrote:
> Russell Lewis wrote:
>> [...]
>> * Invarant-by-default.  That is, an unadorned variable (whether it is
>>   a global variable, function local, or parameter) is always invariant.
>> * Use the keyword 'var' to represent a variable which can be modified.
>> * Use the keyword 'volatile' to indicate that there are aliases of the
>>   variable, so it is not safe to cache the value of the variable in a
>>   register.
>> * 'volatile' is illegal on value-types.  (Only allowed on pointers,
>>   arrays, class references, etc.)
>> [...]
> 
> I think a *lot* of programmers would find it onerous to decorate all mutable identifiers.  I think pure value-style programming is best enabled by a fully supported functional environment, which D does not quite have.  It's still fairly awkward to define lambads and currying is not supported to the extent that it is in mainstream FP langs.  Part of D's elegance is that it is not overly verbose, like Java or Ada or other languages.  I also think that invariant-by-default would totally surprise people who were told that D is an "imperative" language that is "a lot like C++".  Remember that a language is as much a cultural phenomenon as a technical one.
> 
> Dave