Stepping back and looking at constness from another angle.

Derek Parnell derek at nomail.afraid.org
Tue Jun 5 07:09:51 PDT 2007


On Mon, 04 Jun 2007 22:05:13 -0700, David B. Held wrote:

> Let me point out a few guiding principles that are worth considering:
> 
> 1) The more code you break, the more benefit you must offer
> 
> 2) Orthogonality is good; exceptions and corner cases bad

Yep and yep.

> While the idea of making const the default sounds appealing, why limit 
> it to function parameters?  Why not treat variables as const-by-default 
> also?  

Well, function parameters are not 'variables', they are data you entrust to
functions. And 'variables' that are not mutable are not /variables/, they
are constants. In brief, function-parameters and identifiers-in-scope are
not the same beasties. 

However, I get your point. Why not have local data immutable by default? I
would say its because nearly all local data is designed to be modified and
the immutable local stuff is not as common. Whereas most data passed to
functions is designed to be immutable. According to Walter's experience (as
I read it) he is finding that to be true.

> Very functional indeed.  Unfortunately, not very D-like.

A what exactly is "D-like"? 

>  D could 
> have builtin lists, just like functional languages.  Instead, it has 
> arrays.  Guess why?  Arrays are smaller and faster.  D chooses 
> performance.  Those arrays could be immutable by default, but they 
> aren't.  Why not?  Try to time the performance of quicksort in Haskell, 
> and compare it to C's qsort(), and you will soon understand the power of 
> mutable arrays.  Haskell's version certainly looks better, but C's burns 
> the pants off all takers, because it's about as fast as hand-coded, 
> fully optimized, no-holds-barred assembly.

I think the key phrase here is "immutable by default", meaning that a
quicksort in D could be coded with mutable arrays even if the default array
usage is immutable. Haskell doesn't give you that option, no?

> D is a multi-paradigm language, like C++, but let's not forget that 
> "imperative" is a paradigm, and that machine language itself is 
> imperative.  And since D aims to let you get as close to the machine as 
> possible, it makes sense that the default for *variables* is mutability.

As "D is multi-paradigm"
and "imperative is a paradigm"
and "machine language is imperative"
and "D aims to [be close] to the machine"
therefore "it makes sense that the default for *variables* is mutability"

Huh? This doesn't seem to be a logical conclusion at all. Can you expand or
elaborate?

Doesn't 'default' mean something like 'unless otherwise specified'? In
other words, if the default is "X" it does not follow that "not X" is
therefore prohibited, which seems to be behind some of your arguments.
 
> A corollary to principle 2) above is:
> 
> 3) Function parameters should have the same syntax as local declarations

Why? They are not the same things. For example ...

  void Foo(int x)
  {
     int y;
     . . .
  }

Most people reading this would assume that 'x' is not going to be modified
by Foo and that 'y' is going to be modified by Foo. In general, data passed
to functions is not modified by the function - but there are exceptions,
and in general local declarations are modified by the function - but there
are exceptions.


> Why?  Well, why not?  Why should users have to think hard about whether 
> a particular syntax is only legal for parameters or not?  

Why 'legal'? Are we talking about 'default' access modifiers rather than
what syntax is allowed?

   void Foo(upd int x)
   {
      const int y = 42;
      . . .
   }

Above, the default modifiers have been replaced by explicit modifiers. Now
the reader knows that 'x' could be modified by Foo and 'y' would be
modified by Foo.


> While certain 
> qualifiers like 'in' and 'out' must necessarily be exceptions, they idea 
> is that they provide sufficient convenience to justify their existence. 
>   Beyond that, you should be able to look at a function parameter list 
> and say: "Each of those parameter declarations could be pulled out into 
> a local declaration".

One still can. But I think you mean "pulled out" without changing the
declaration source text. If one is doing such refactoring then one needs to
re-examine the code anyway so text changes are to be expected, I believe.

> If we agree that is a powerful principle that makes the language easier 
> to understand, then we see why it is not acceptable for function 
> arguments to default to const while locals do not.  

Actually I don't. I do agree that orthogonality is a good principle for
programming languages to follow, but not if it makes languages harder to
understand. I also work with the Euphoria programming language. It is
strict with respect to parameters - any change to parameter data made by a
function is never returned to the caller. In other words, the caller can
always know that whatever data they pass to a function will not be changed
by that function. In Euphoria, parameters are immutable and local
declarations are mutable. This is never been a concept that has been hard
to grasp by Euphoria practitioners.

> And if we agree that 
> D is an imperative language before all else, because it is a *systems* 
> language, then we see why it is not acceptable for locals to default to 
> const.  

I still don't follow this line of 'logic'. We are talking about a compiler
that can make intelligent choices and we are talking about 'defaults'. But
I agree for other reasons that local declarations should be mutable by
default.

> This leads us to conclude that mutability should indeed be the 
> default, and that constness should be indicated explicitly.  

 ... for local declarations.

> However, I 
> think 'in' as a synonym for constness is a very nice compromise that 
> gives just about the shortest possible abbreviation for 'const' that you 
> could imagine (short of something silly like type sigils).

An even shorter abbreviation for the concept of immutable function
parameters is to omit the access modifier. And if that is more common than
mutable function parameters it must mean less typing and less for code
readers to process.

> One person mentioned that Perl doesn't have 'const'.  In fact, that is 
> only half-true.  There is a Perl pragma called 'constant' which is used 
> like thus:
> 
> use constant PI => 3.14159;
> 
> Unfortunately, being a pragma, the symbol PI does not interpolate like a 
> scalar, making certain uses of it very awkward.  Larry recognized both 
> that this was unacceptable and that constants are indeed very useful, 
> and decided to give Perl 6 first-class constants.
> 
> Also, Andrei likes to make much of the fact that Perl, like Java, has 
> immutable strings.  Who would have guessed?  Perl is pretty much the 
> poster child of string manipulation and obscure do-anything code 
> semantics, so unless you went looking for it, very few people would 
> guess that strings in Perl are actually immutable, but they are.  Why is 
> that?  Well, it's because making strings CONST makes it very easy to 
> guarantee that there is no improper aliasing mixed with string mutation.
> 
> Java programmers that need mutable strings use StringBuilder, and then 
> convert to a String when they're done.  It's such a powerful idiom that 
> the xxxBuilder pattern is quite popular in the Java standard library 
> now.  How could D get immutable strings?  Well, we could try it with 
> const, but if you look closely, 'const' doesn't mean "this array can't 
> change".  It means: "You can't change this array through this const 
> reference, but somebody else can."  That is, 'const' isn't strong enough 
> to get you immutable strings.  For that, you need something stronger. 
> You need 'invariant'.  Only invariant guarantees that nobody holds a 
> mutable reference to your object, and thus, you are free to alias the 
> object all you like, quite safely.  D, being a *systems language*, 
> concerned with *performance*, will take any performance-enhancing 
> substance available, and 'invariant' is one of the best drugs on the 
> market (eat your heart out, Floyd).
> 
> So could we live with 'invariant' and dump 'const'?  Well, we could, but 
> it wouldn't result in a very useful language.  Because then we wouldn't 
> be able to say: "I want the implementor of this function to have a 
> read-only view on my precious mutable data which I need to be able to 
> update in place."  The only way you could do that is to make a copy, 
> which defeats the whole point of 'invariant'.  Are things starting to 
> become a little more clear?

Not at all <G>

Now you seem to be contradicting your earlier arguments. It now seems that
you are saying that sometimes default immutable local declarations are a
good thing. 

(N.B. I'm trying to avoid using the terms 'const' and 'invariant' because
these have specific meanings depending on which language one is talking
about.)
 
With respect to strings above, it seems you are saying that we need a
mechanism to identifier those strings (arrays?) that are always immutable
during the life of a program, and those accesses to strings (arrays?) that
are not given permission to change them.

One is talking about any and every access to the data (invariant) and the
other is talking about specific accesses to data (const).

> So if we agree that the Functional Paradigm is A Good Thing, and part of 
> why it's a good thing is Purity, then it follows that D can gain some of 
> the benefits of FP by implementing limited purity, which is what 
> const/invariant are.  Why is purity a good thing?  It's very simple. 
> Functions, in the mathematical sense of a map from A to B, are fairly 
> easy to understand.  State machines, on the other hand, are much much 
> harder to understand.  Adding variables to a state machine typically 
> increases the number of states geometrically, and the number of state 
> transitions exponentially.  Adding constants, on the other hand, does 
> not increase the number of states, nor the number of transitions, which 
> means that you literally get an exponential increase in simplicity by 
> making as many things immutable as possible.  And that's why 'const' in 
> general is A Good Thing(TM).

I don't think that anyone here has a problem with the concept of limiting
the type of access to data that functions can have. The discussion is more
about should the default access permission be 'immutable' or 'mutable'?

> Of course, the cost of all this is some additional syntactic burden. 
> But consider the opposite case: pure functional languages trying to 
> implement mutable state.  In our case, we only need to sprinkle a 
> keyword here and there.  For Haskell, you need to *define a Monad*.  In 
> case you didn't know, a Monad is a structure with three distinct 
> operations which are fairly non-trivial for a novice to define.  On the 
> other hand, most novices can add 'const' to a declaration without any 
> assistance whatsoever.  This is most likely why even most functional 
> languages are not pure.

I think you've gotten off the track a bit. No one is saying that D can only
support immutable function parameters, but just that the default access
permission should be immutable. If one needed to have mutable function
parameters then one would explicitly say so. The talk about Haskell is a
red-herring - a straw man argument, if you like.

> If you think that you are willing to take the plunge and make all your 
> programs const by default, consider all the libraries that D can and 
> does link to, and consider the effect of const-by-default on that 
> codebase.  Even if you are willing to use a Haskell-like discipline 
> yourself, most D code is not, and would take a huge effort to fix up.

Got any figures to back this assertion up? I don't mind being proven wrong.

> One final observation is this: the benefit of const is something of an 
> emergent property that is sensitive to scale.  That is, for small 
> programs, the cost outweighs the benefit.  But as a program grows from a 
> few modules to several libraries and tens or hundreds of modules with 
> many thousands of lines of code written by teams of programmers, the 
> benefit of const pays back its cost through every possible interaction 
> of programmers.  Why is that?  It's because 'const' is both 
> documentation and a promise.  It tells another programmer what to 
> expect, and it promises to follow through on that created expectation. 
> Since the number of possible interactions among programmers is factorial 
> (super-exponential!) in the number of coders, the benefit of const soon 
> becomes apparent with even a modestly-sized team.

Are you talking about the key word 'const' or the concept of immutability?
If immutability was the default the function contracts still exist. The
'documentation' is still in the source code as one can still determine
which function arguments are mutable and which are not.

> If you have reservations about const on your personal project, try to 
> imagine writing a very large project with a group and ask yourself how 
> much you trust the other programmers (imagine the group stepped out of 
> Dilbert, and not your favorite startup).  While inferring 'const' is 
> certainly possible (and several papers have been written about how to do 
> it), it neglects the documentation aspect of 'const'--the contractual 
> aspect of it.  This is something that only becomes apparent at large 
> scales, but once you reach those scales, it becomes very visible very 
> quickly.

Why is it necessary, in the philosophical sense, to have the key word
'const' explicitly coded to implement the concept of immutable function
parameters?

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
5/06/2007 11:04:11 PM



More information about the Digitalmars-d mailing list