January 21, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Dave wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> Casts can break any design, so it's not phasing anyone off that they can break const. Besides, the fact that Const holds a pointer to the object as its first element is not something client code is allowed to rely on. This is a non-issue.
>>
>> I wish Walter would have agreed with many of us that it (casts breaking any design) is a "non-issue" for going ahead with a 'semantically meaningful const' a year ago. I hope you can convince him!
> 
> My statement about design-breaking casts needs qualifying. Walter (and I entirely agree with him) dislikes the fact that C++ actually _legalizes_ const removal via a cast. Consider the following C++ code:
> 
> void Foo(const int & x)
> {
>   const_cast<int&>(x) = 0;
> )
> 
> int main()
> {
>   int x = 1;
>   Foo(x);
> }
> 
> This is 100% legal C++ code with defined behavior; casting away const is ok if the initial data was not const. This feature kicks const straight in the proverbials.
> 

I think the general understanding was that D const would not be C++'s const (with all of the warts like the one you pointed out), but _also_ would need to go further and be a "guarantee".

> A "design-breaking" cast is different: the language does not guarantee the behavior of code that uses it, and therefore can act under the assumption that it will not happen. This is key to introducing const in D in a manner that is useful to everybody.
> 

IIRC, Walter has made mention of const truly being 'readonly', even to the point of wishing for some way to enforce it with hardware or OS support. It should be in the archives somewhere. Like I said I hope he can be or already is convinced otherwise since hardware / OS support isn't available, at least on the most popular platforms.

>>>> Both "user-space" and "compiler-space" const has been discussed at length in the past, and Walter's response has always been (paraphrasing) "D won't have const unless it is semantically meaningful (100% enforceable) by the compiler", which will never happen with a language like D.
>>>
>>> This statement must be understood in spirit. The spirit is, D shouldn't add a feature that fails spectacularly at providing a true guarantee, as C++ did. The design I suggested does without the actual feature.
>>>
>>
>> I completely agree with this sentiment. In earlier discussions, there were designs that went even further, for example not being able to take the address of or cast from a const parameter, etc.
>>
>> But IMO that sentiment has been subjectively and unevenly applied to different but often requested language requests over the years. Unfortunately the earlier const suggestions were a case where the same sentiment was not applied. IIRC, the majority of the "group" - many of which had a large amount of experience writing libraries with D - strongly favored a 'const' or 'readonly' even though the compiler could not always guarantee they couldn't be modified.
> 
> It is then possible they were wrong, or that they didn't come with a reasonable design. Speaking of which (famous last words to come), I don't want to sound harsh, but if there is one thing that's worse than language design by committee, that's got to be language design by community. Having too many cooks in the kitchen dilutes vision, which is probably the single most important thing in a language design. The few places where Walter made populist decisions are eminently visible in the language design (ah, "length", gotta love that one), and many feature requests that have been aired on this newsgroup are in dire need of design.
> 

I should qualify "designs".. There were a few posts that may be considered full-fledged "designs", but most of them were just ideas put forward on const and how to make it generally usable. The problem always came to be that none of them could guarantee what was understood as a prerequisite from Walter - truly read-only.

> 
> Andrei
January 21, 2007
Dave wrote:
> IIRC, Walter has made mention of const truly being 'readonly', even to the point of wishing for some way to enforce it with hardware or OS support. It should be in the archives somewhere. Like I said I hope he can be or already is convinced otherwise since hardware / OS support isn't available, at least on the most popular platforms.

OS support is often available, but in too coarse a manner (page-level read-only) to be used pervasively at language level. C++ compilers of today go as far as massaging all read-only data of an executable in read-only pages, which has been quite effective, yet not applicable at a finer scale.

Things have improved with regards to understanding the value of const. It turns out that true read-only is the least interesting of const's benefits, and that 'view-only domains' is a much more powerful paradigm.  D will have const. The main issues wrt const are (1) fixing the inout disaster, (2) figuring a semantics for const that works with the current D and devising the appropriate blend of deduction and user-level annotation (C++ has too little of the former and imposes too much of the latter), and (3) figuring a priority of the implementation on D's busy agenda, where there are many much more exciting features than const.

> I should qualify "designs".. There were a few posts that may be considered full-fledged "designs", but most of them were just ideas put forward on const and how to make it generally usable. The problem always came to be that none of them could guarantee what was understood as a prerequisite from Walter - truly read-only.

Or none of them converted that lack of guarantee miraculously into a feature :o). (I'm more serious than I might seem!)


Andrei
January 21, 2007
Dave wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> My statement about design-breaking casts needs qualifying. Walter (and I entirely agree with him) dislikes the fact that C++ actually _legalizes_ const removal via a cast. Consider the following C++ code:
>>
>> void Foo(const int & x)
>> {
>>   const_cast<int&>(x) = 0;
>> )
>>
>> int main()
>> {
>>   int x = 1;
>>   Foo(x);
>> }
>>
>> This is 100% legal C++ code with defined behavior; casting away const is ok if the initial data was not const. This feature kicks const straight in the proverbials.
> I think the general understanding was that D const would not be C++'s const (with all of the warts like the one you pointed out), but _also_ would need to go further and be a "guarantee".

My objections to the C++ const are:

1) there are *legal* ways to subvert it, meaning it is useless as a semantic aid and useless to the optimizer/code generator
2) it confuses a type modifier with a storage class (exhibiting muddled special case behaviors)
3) it's ugly and litters declarations
4) the use of overloading based on const is a workaround to another design flaw in C++

I've stated that I would put a const in if we could find a design without those shortcomings. I also believe D is better off with no const at all rather than a half-assed one.

>> A "design-breaking" cast is different: the language does not guarantee the behavior of code that uses it, and therefore can act under the assumption that it will not happen. This is key to introducing const in D in a manner that is useful to everybody.
>>
> 
> IIRC, Walter has made mention of const truly being 'readonly', even to the point of wishing for some way to enforce it with hardware or OS support. It should be in the archives somewhere. Like I said I hope he can be or already is convinced otherwise since hardware / OS support isn't available, at least on the most popular platforms.

The idea is that if the compiler did put all the const data into readonly memory, that it would not change the behavior of a conforming program. In other words, it would be undefined behavior to deliberately subvert the const via casting, inline assembler, or pointer manipulation.

This is quite different from C++, where (as Andrei pointed out) it is legal, defined behavior to subvert const.

> I should qualify "designs".. There were a few posts that may be considered full-fledged "designs", but most of them were just ideas put forward on const and how to make it generally usable. The problem always came to be that none of them could guarantee what was understood as a prerequisite from Walter - truly read-only.

Designs and proposals are posted every day in this n.g. It's simply not possible to implement them all, or even give coherent replies to them all. But these n.g. discussions are valuable - the lazy parameter and scope guard designs were significantly improved as a direct result. The way import now works is a direct result of the n.g., for just 3 of many examples.
January 21, 2007
janderson wrote:
> janderson wrote:
>> Dave wrote:
> 
> 
> Also:
> 
> Something like ned-malloc in the standard lib.
> 
> -Joel

I should add, compiler identifying cases where:

1) An allocation doesn't need to be put into the GC (ie it could be placed on the stack.)
2) Compiler work out at compile time when a particular piece of memory could be deleted (rather then complete GC).  Ok we have auto however I'm talking about places where this can't be used.

Of course what these cases are, needs more thought (and could be optional).  However this is no where near the top of my performance wish list.

-Joel
January 21, 2007
Andrei Alexandrescu (See Website for Email) wrote:
> 
> The problem is that D does not have a clear stance on what a storage
> class is, and how it is to be manipulated statically. It is unclear
> whether storage is part of the type or a separate attribute of an
> expression. If this is not solved, it will inflict unbounded amounts of
> pain onto template code. Wiring more storage classes (such as 'lazy'
> which I realized Wednesday was a bad idea as it murks things even more
> and is thoroughly ill-designed because it is a storage class that
> actually changes the syntax of the object use -- someday Walter should
> stand the pressure and stop adding features just because people say they
> seem cool) into the language is not going to help any. Heck that was a
> long sentence :o).
> 
> 
> Andrei
> 
What's wrong with 'lazy'? 'lazy' is not a storage class. Its behavior only seems strange if you think of it as a storage class (i.e., some that does not fundamentally change the underlying type).

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
January 21, 2007
"Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail@erdani.org> wrote in message news:eotmt2$2fru$3@digitaldaemon.com...
> The reflection mechanism would have to provide the information whether or not a given member function changes the object.

How would that work? The difference between getters and setters is probably easy to see, but what about member functions calling non-member functions? Is the signature of that function (in,out,inout) enough to determine mutability? Sounds like a C++-style const afterall.

And how about the nested classes, or array members? I very much like the idea though: generating getters with the same name as the members, but that alone is not enough.

I get very confused thinking about this : S

L.


January 21, 2007
Andrei Alexandrescu (See Website for Email) wrote:
> The reflection mechanism would have to provide the information whether
> or not a given member function changes the object.
> 
> The only drawback that I can think right now is that the compiler can't
> exploit this kind of constness with ease to generate better code; it's a
> "user-space" implementation with semantics that are hard to figure out
> at the compiler level.
> 
> A minor drawback is that Const!(Foo) must be implicitly constructible
> from a Foo, but another in-design language feature (opImplicitCast) will
> take care of that.

Correct me if I'm wrong, but I think none of this stuff will work for methods that are only declared instead of defined as well; 'const', implemented like this, wouldn't work for "header modules"...

For instance:
-----
struct Foo {
    private int data;
    char[] bar();
}
-----
When implemented like you suggest, I don't see any way that Foo.bar() can possibly be determined to not change the struct. So will Const!(Foo).bar exist?
If not (to be on the safe side) that would seem to limit the use of const for something like a closed-source library.
If it *does* exist, that would easily (and perhaps accidentally) circumvent the meaning of const.


I also don't see a way this can work for classes (in general). There's no way to limit a non-final method to non-mutating operations that I'm aware of, so it could easily be circumvented by subclassing. Even though that would only allow direct access to non-private fields, mutating base class methods could still be called for the rest (if available). So then the only way to get a const version of a class would be if the class doesn't allow mutations anyway (like java's String). Which leaves us right back where we started :(.

And I don't think a 'const' implementation should work for structs and not for classes...
January 21, 2007
Lionello Lunesu wrote:
> "Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail@erdani.org> wrote in message news:eotmt2$2fru$3@digitaldaemon.com...
>> The reflection mechanism would have to provide the information whether
>> or not a given member function changes the object.
> 
> How would that work? The difference between getters and setters is probably easy to see, but what about member functions calling non-member functions? Is the signature of that function (in,out,inout) enough to determine mutability? Sounds like a C++-style const afterall.

Except that it's deduced. Think of it the other way: if const is an explicit Boolean flag that the compiler can check, it makes sense to have the compiler simply infer it.

> And how about the nested classes, or array members? I very much like the idea though: generating getters with the same name as the members, but that alone is not enough.
> 
> I get very confused thinking about this : S

Me too. I actually thought lazy is a storage class. It sure has the syntax of one in definition. It changes the use syntax (requires parens). And if I look at

http://www.digitalmars.com/d/lazy-evaluation.html

I see that lazy *is* a storage class.

My original suggestion was to simply convert expressions to delegates automatically. That setup has its own problems, but I think it's much easier to deal with them than it is with lazy.

A template can't detect lazy properly. It's unclear what it means to assign to a lazy parameter. Lazy is a storage class that changes the access syntax. I believe lazy is ill-designed. Walter should have stood the pressure and refuse to implement it without a clear definition.


Andrei
January 21, 2007
Frits van Bommel wrote:
> Andrei Alexandrescu (See Website for Email) wrote:
>> The reflection mechanism would have to provide the information whether
>> or not a given member function changes the object.
>>
>> The only drawback that I can think right now is that the compiler can't
>> exploit this kind of constness with ease to generate better code; it's a
>> "user-space" implementation with semantics that are hard to figure out
>> at the compiler level.
>>
>> A minor drawback is that Const!(Foo) must be implicitly constructible
>> from a Foo, but another in-design language feature (opImplicitCast) will
>> take care of that.
> 
> Correct me if I'm wrong, but I think none of this stuff will work for methods that are only declared instead of defined as well; 'const', implemented like this, wouldn't work for "header modules"...

That is correct. Interface functions, nonfinal methods, and declared-only functions must be annotated manually. The same holds for things like "pure". Hopefully they are only a fraction of the entire const use, such that the burden of the programmer is reduced.


Andrei
January 21, 2007
Walter Bright wrote:
> My objections to the C++ const are:
> 
> 1) there are *legal* ways to subvert it, meaning it is useless as a semantic aid and useless to the optimizer/code generator
> 2) it confuses a type modifier with a storage class (exhibiting muddled special case behaviors)

Let me point out that D's current handling of the storage classes is broken. D defines the 'inout' and 'out' storage classes which are undetectable for templates and break any template code that needs to deal with them. (Why in the world was 'out' ever needed? Probably the "working with IDL" assumption should be revisited.) Then, to add insult to injury, D defines the 'lazy' storage class that changes the access syntax (something that a storage class is not supposed to do), does not allow assignment from the same type (although it should), and again can't work with any template code.

> 3) it's ugly and litters declarations
> 4) the use of overloading based on const is a workaround to another design flaw in C++

But right now D is worse off than C++ because in D you can't overload on inout (or detect it in other ways) except for a much contorted way that I recall I've seen in a post around here. So where C++ is simply verbose, D is knee-deep in the mud.

> I've stated that I would put a const in if we could find a design without those shortcomings. I also believe D is better off with no const at all rather than a half-assed one.

Yup. We need a full-assed one :o).

>> I should qualify "designs".. There were a few posts that may be considered full-fledged "designs", but most of them were just ideas put forward on const and how to make it generally usable. The problem always came to be that none of them could guarantee what was understood as a prerequisite from Walter - truly read-only.
> 
> Designs and proposals are posted every day in this n.g. It's simply not possible to implement them all, or even give coherent replies to them all. But these n.g. discussions are valuable - the lazy parameter and scope guard designs were significantly improved as a direct result. The way import now works is a direct result of the n.g., for just 3 of many examples.

I don't know much about import, am in favor of scope guard's improved syntax (compared to the one I proposed), and I'm positive that lazy is the epitome of bad language design and should go away. The article:

http://www.digitalmars.com/d/lazy-evaluation.html

proudly acknowledges the post:

http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=41633

which doesn't make any sense of anything, confuses lazy/delegates with expression templates, and fails to provide both a solid argument for 'lazy' and the shade of a valid design for it. The net result is:

- lazy is a full-fledged type constructor coming in the disguise of a storage class;

- lazy cannot be assigned even from same type, nor from a delegate, which reduces templates' ability to manipulate lazy and non-lazy stuff uniformly;

- templates are powerless with lazy, as they are with any other storage class; they are doubly powerless with lazy because they now need to deal with the modified access syntax.

I believe that populism in language design is not good.


Andrei