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