www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - 'var' and 'volatile' as !const, with invariant-by-default

reply Russell Lewis <webmaster villagersonline.com> writes:
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
Jun 25 2007
next sibling parent reply BCS <ao pathlink.com> writes:
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
 

Jun 25 2007
parent reply Russell Lewis <webmaster villagersonline.com> writes:
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


Jun 25 2007
parent reply BCS <ao pathlink.com> writes:
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
 


Jun 25 2007
parent Russell Lewis <webmaster villagersonline.com> writes:
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.
Jun 25 2007
prev sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
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
Jun 25 2007
parent Russell Lewis <webmaster villagersonline.com> writes:
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

Jun 26 2007