Ramifications of const

Georg Wrede georg at nospam.org
Wed Jun 13 05:13:01 PDT 2007


Bill Baxter wrote:
> Georg Wrede wrote:
> 
>> BCS wrote:
>>
>>> Reply to Georg,
>>>
>>>> I think parameter passing is a world separate from "other const or
>>>> const-as-default" areas. Thus, having parameters behave differently
>>>> from them poses no risk for confusion, and, IMHO, is a conceptually
>>>> clean and intuitive choice. It also has a crystal clear perimeter,
>>>> which dramatically reduces the mental strain of having to remember
>>>> things.
>>>
>>>
>>> The issue is that a and b in this code are of different types if T is 
>>> a reference type and that same type otherwise.
>>>
>>> |void TFn(T)(T a)
>>> |{
>>> |  T b;
>>> |}
>>
>>
>> (Disclaimer: I've been out of town some, so I might have missed 
>> something important relating to this.)
>>
>> Within the function (prototype or not), T a would be a read-only 
>> variable and T b would be the correspoinding read/write variable.
>>
>> Could you give me an example where this does pose a problem?
> 
> 
> I think the big trouble here comes just from having const at all, not 
> because of it being the default.
> 
> void TFn(T)(T a)
> {
>   T b;
> }
> 
> If T is a reference type and you want a to be const, and b to not be 
> const, then you've got some explaining to do either way.  But probably 
> the most common case is that you want the parameter to be non-modifiable 
> and the local variable to be modifiable.  And there const by default works.
> 
> There is a question about how to handle IFTI, which I believe the other 
> const-by-default examples (Perl6 and Euphoria) probably don't have to 
> deal with.   The question is should this:
>     int x;
>     TFn(&x);
> 
> deduce T as const-final-scope int* or just plain int*.  Regardless of 
> the const-by-default issue, D will need a simple way to go back and 
> forth between 'in T' and 'mutable T' so I think deciding that IFTI works 
> either way would be fine.  But the IFTI issue definitely needs some more 
> thinking.

Suppose we consider a function to be a soap bubble. Variables declared 
inside it are r/w unless the programmer for some reason wants otherwise. 
And the variables that are arguments to this function are read-only, as 
seen from within the function, unless the author wants to specifically 
declare them otherwise.

Let's first think about functions without side effects, and then 
functions with side effects.

A function without side effects reads stuff from the arguments, 
manipulates the values and then possibly returns a result. During this 
manipulation, the arguments are only used to hold the parameters given 
to the function, and every time arithmetic or something is done to them, 
the result is stored into a local variable. The result of the function 
is stored in a local (possibly implicit) variable that is then passed as 
the return value. Such return values are treated as read-only by the 
calling function because in C family languages they are stored on the 
stack, and thus have to be directly assigned to a variable (or an 
implicit variable, say in an arithmetic context, like the return value 
of sin(x)) or else the value is lost.

Currently I can't find any problem with this.


Then we have the functions with side effects. These functions can affect 
either their arguments, or something "external", like memory or IO.


Let's look at modifying arguments. The trival example is inc(x).

When passed a value, it modifies the value and exits. Still simple. But 
what happens when it is passed a reference to a value? Should it be able 
to change the reference or the referree? Or both?

One could spend weeks pondering on this, with numerous examples for any 
of the three possibilities. But I think such pondering is besides the 
point. (!)

The one important distinction is whether _anything_ gets changed via the 
argument mechanism, or not. In other words, either a function does not 
touch what it gets, or then it does. What it does change is very much 
less important than the fact itself that it does.

If we accept this line of thinking, then we have, on one side, the 
functions that don't, and which thus can be "relied upon" and also 
optimised during compilation. On the other side we have the functions 
that do, and for which the programmer has to consult documentation or 
source code -- or simply understand from the context (as with inc(x), 
where the intent is self evident) what is going to be modified.

At this point I'm not sure there is a genuine need to have the compiler 
make a distinction between modifying a reference itself or the 
pointed-to value. (Anybody having a good counter example is welcome 
here.) At any rate, only having the single distinction between argument 
modifying and non-modifying is immensely useful. We get most of the 
candy with just two cents, whereas placing restrictions between the 
different ways of modifying stuff via an argument does cost bucks and it 
brings only small incremental benefits. (C++ gives you choices we don't 
need: strap yourself immobile, shoot yourself in the foot, or drown in 
the sea of implications.) KISS.


Now, to the functions that modify stuff "outside" the arguments.

A trivial example would be a function that changes the system time, 
setTime(y,m,d,h,m,s). None of the arguments as such are modified, but 
something else is. (There are also functions that both modify their 
arguments and modify "outside stuff". They don't need to be addressed 
here separately.)


Conclusion

For the purposes of language development, we now have two attributes for 
each function: does it change its arguments, and does it change anything 
else. I'd actually like to have these incorporated into the function 
signature. A function can only be Pure if all the functions it calls are 
Pure (i.e. do not change anything) and it is Pure itself.

It might be useful to have "does chanege its arguments" as a flag which 
can quickly be checked during compilation, even if it is deducable from 
the argument declarations. This may speed things up. But the *more* 
important flag would be whether the function may change "external" 
things. This is hard to see without a flag in the signature.


I see that pure functions will gain more importance in the near future 
of D, and therefore we should prepare for it now with this flag.



More information about the Digitalmars-d mailing list