www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - For 2.0, or D++: A Thought about Class References & Pointers

reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I'm becoming increasingly convinced that not requiring * for class 
reference variables was a mistake.  In other words, I think that the 
syntax for declaring a new class variable on the heap should have been:
	MyClass *ptr = new MyClass;
Note that I'm not suggesting that you should be able to use class 
variables directly (since that would require copy constructors and other 
nightmares) - instead, I'm suggesting that we keep the same language 
semantics, but simply require a '*' for all class reference variables.



I have a couple of reasons for this:

1) Templates
The first one I remember coming across was template code.  Say you want 
to work with something by reference.  If the thing is a class, then you 
must use the syntax "T foo" while if it is anything else, you must use 
the syntax "T* foo".  It would be better if there was consistency.  If 
there is an '*', then it is a pointer to something.  If there is not, 
then it is a literal variable.

2) Newbie learning curve
Many people have posted questions on the newsgroup, asking why their 
code segfaults when they write code like this:
	MyClass foo;
	foo.DoStuff();
We don't know how many more people just got turned off to D and never 
asked the question here.  If the '*' were required, then newbies would 
learn quickly that the right thing to do is:
	MyClass *foo = new MyClass;
	foo.DoStuff();

3) Objects on Heap
We've had many long discussions about objects on the heap.  The fact 
that D lacked them lead to 'auto' variables, and still people want more. 
  Frankly, I think that objects on the heap make a lot of sense 
sometimes;  we could have them and still avoid the copy constructor stuff.

What if the compiler let you write this code:
	MyClass foo;
which was syntax sugar for this:
	/* on stack */ MyClass _implicit_foo;
	/* ref */ MyClass *foo = &_implicit_foo;

The class object would be constructed on the stack.  Like 'auto', it 
would be automatically destroyed when the variable goes out of scope.

The 'foo' variable would work pretty much like an "out" parameter to a 
function.  Internally, it would be a pointer; however, you could use it 
like a value variable.  So you could write this code:
	MyClass foo;
	MyClass *ptr = &foo;

Moreover, you make these rules:
	* It is illegal to ever assign a value to a class variable.
	  (You can assign values to pointers to classes.)
	  So this code is illegal:
		MyClass a,b;
		a = b; /* syntax error */
	* You can never have a class variable as a function arg.
	  (You can have a pointer to a class as an arg.)
Then, we don't have the copy constructor problem.  If you want to pass 
an object to a function, you must pass a pointer to it, not the actual 
value.



***NOTE NOTE NOTE NOTE***
I want to say that I am still very much in support of D.  I think that 
it is the best language out there (by far) and I use it as much as I'm 
able.  So please, I'm not saying that "D is broken" - far from it. 
However, I think that the next generation language, be it D 2.0 or D++, 
should consider this argument and maybe make the change.
Aug 25 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <cgj8er$2lat$1 digitaldaemon.com>, Russ Lewis says...
1) Templates
What about "inout T foo"
2) Newbie learning curve
While I'm a C/C++ person, I don't agree with this argument. The same could be said of Java and yet folks have picked it up easily enough. I think this is just a language detail that people have to learn.
3) Objects on Heap
We've had many long discussions about objects on the heap.  The fact 
that D lacked them lead to 'auto' variables, and still people want more.
I think all people want is some guarantee that an auto object they construct is on the stack, and that is something I am not convinced is necessary. If something is declared as auto then I think it's fair to let the compiler decide where to put it. The stack is a completely viable option for compilers who want to optimize performance a bit.
Then, we don't have the copy constructor problem.  If you want to pass 
an object to a function, you must pass a pointer to it, not the actual 
value.
I don't know, I kind of like D's current semantics. But it might be interesting to experiment with some sort of official copy constructor semantics. Or perhaps COW is the way to go for most instances. Either way, I'm not sure I'm keen on bringing more C pointer terminology into D than it already has. Sean
Aug 25 2004
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Sean Kelly wrote:
 In article <cgj8er$2lat$1 digitaldaemon.com>, Russ Lewis says...
 
1) Templates
What about "inout T foo"
And if you declare local variables? inout doesn't help me then.
2) Newbie learning curve
While I'm a C/C++ person, I don't agree with this argument. The same could be said of Java and yet folks have picked it up easily enough. I think this is just a language detail that people have to learn.
I understand that argument; yet I ask the question: why design a language element such that people "have" to learn it? Why do something that almost guarantees that people coming from C++ will struggle with segfaults for a while before they get working code? I think that if we do what I suggest here, the compiler can instruct the coder with its syntax error messages: If we don't allow objects on the stack (as below), then when a newbie first writes: MyClass foo; the compiler can respond with an error "ERROR: Class objects cannot be allocated on the heap. Use a pointer variable, and allocate an object with operator new instead." Bingo! The newbie immediately knows what to do, with no segfaults. OTOH, if we allow objects on the heap, then C++ programmers come over and it just works the way they expect. The first time that they try to assign something: MyClass bar,baz; bar = baz; the compiler can tell them "ERROR: Class variables cannot be assigned. Use pointers to classes instead." And now the C++ programmers know exactly how to use the language as well.
Aug 26 2004
prev sibling next sibling parent reply Matthias Becker <Matthias_member pathlink.com> writes:
I have a couple of reasons for this:

1) Templates
The first one I remember coming across was template code.  Say you want 
to work with something by reference.  If the thing is a class, then you 
must use the syntax "T foo" while if it is anything else, you must use 
the syntax "T* foo".  It would be better if there was consistency.  If 
there is an '*', then it is a pointer to something.  If there is not, 
then it is a literal variable.
What about the inconsitency of op= ? Sometimes it's a flat copy sometimes a reference assignment. So do we then get foo = bar; // reference assignment *foo = *bar; // flat copy ???
Aug 26 2004
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Matthias Becker wrote:
I have a couple of reasons for this:

1) Templates
The first one I remember coming across was template code.  Say you want 
to work with something by reference.  If the thing is a class, then you 
must use the syntax "T foo" while if it is anything else, you must use 
the syntax "T* foo".  It would be better if there was consistency.  If 
there is an '*', then it is a pointer to something.  If there is not, 
then it is a literal variable.
What about the inconsitency of op= ? Sometimes it's a flat copy sometimes a reference assignment. So do we then get foo = bar; // reference assignment *foo = *bar; // flat copy ???
I'm not 100% clear whether you're agreeing with me, or arguing. :) So I'll try to state my view about this subject... This example is just another reason why lacking the '*' makes templates hard to write. If you write this code: template <T> void foo(T arg) { T mine = arg; ... } is 'mine' a copy of the value in arg, or a copy of a reference? It would be better if we knew if we were assigning a reference or a value.
Aug 26 2004
prev sibling next sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Russ Lewis schrieb:
 I'm becoming increasingly convinced that not requiring * for class 
 reference variables was a mistake.  In other words, I think that the 
 syntax for declaring a new class variable on the heap should have been:
     MyClass *ptr = new MyClass;
Yikes.
 I have a couple of reasons for this:
 
 1) Templates
The official position by Walter was that one usually needs 2 templates in the case of classes and structs. In general, i think templates play an important role in D by allowing to parametrically duplicate some code - something that wasn't possible in Delphi and thus would lead to unsafe containers or every project having to write out (duplicate manually) ThisTypeList and ThatTypeList and ThisTypeBlah in masses - that's what we get to avoid. But i'm not so sure that making templates have the same role as in C++, being the universal hole-filler for everything, makes much sense.
 2) Newbie learning curve
The newbies would just not learn D if we do as you suggest. :) Pointer on classes and combined struct/ class semantics is one of the most confusing things for newbees in C++, so the Delphi and Java syntax/ semantics like we have now is much newbee-safer. But your suggestion is by far more confusing, i believe. I just try to put myself in the position of a newbee. Another thing is that if we do as you suggest, people coming from C++ would requiere that we implement a complete C++ struct/class semantics, and not just allow to use objects by pointer.
 3) Objects on Heap
 We've had many long discussions about objects on the heap.  The fact 
 that D lacked them lead to 'auto' variables, and still people want more. 
  Frankly, I think that objects on the heap make a lot of sense 
 sometimes;  we could have them and still avoid the copy constructor stuff.
Objects on the heap? they are on the heap. Perhaps you mean on the stack? And hey, it is so common to read in C++ libraries "do not construct this on the stack else this breaks". There are just so many things that are better off being on the heap - especially in D where the performance of the garbage collector scan can be severely improved for the heap but not much for the stack. Besides, the implementation would be with hoops and advert effect on performance, since D tries to reduce the number of implicit finalization blocks which are inserted by C++ all over the place. That's apparently the reason why Walter doesn't want structs to have destructors. Perhaps some better support for auto classes would be desired. I'd say it's preferred against adding more stack functionality to usual classes or destructors to structs, since it will not have the "surprising" advert performance effect, and the usage is somewhat better documented that way. And it's less ugly. :)
 Moreover, you make these rules:
     * It is illegal to ever assign a value to a class variable.
       (You can assign values to pointers to classes.)
       So this code is illegal:
         MyClass a,b;
         a = b; /* syntax error */
     * You can never have a class variable as a function arg.
       (You can have a pointer to a class as an arg.)
 Then, we don't have the copy constructor problem.  If you want to pass 
 an object to a function, you must pass a pointer to it, not the actual 
 value.
Hmmm. What it all gives us, that we have to decorate all the common usage cases with a "*", and where the traditional syntax is left it is a bug. If we do anything like that, it should be with a special syntax for the special case, not the other way around.
 ***NOTE NOTE NOTE NOTE***
 I want to say that I am still very much in support of D.  I think that 
 it is the best language out there (by far) and I use it as much as I'm 
 able.  So please, I'm not saying that "D is broken" - far from it. 
 However, I think that the next generation language, be it D 2.0 or D++, 
 should consider this argument and maybe make the change.
Noone is accusing you of anything. After all, it is just opinions. Luckily, we have Walter who would not let anyone just spoil his language. :) -eye
Aug 26 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ilya Minkov wrote:
 3) Objects on Heap
 We've had many long discussions about objects on the heap.  The fact 
 that D lacked them lead to 'auto' variables, and still people want 
 more.  Frankly, I think that objects on the heap make a lot of sense 
 sometimes;  we could have them and still avoid the copy constructor 
 stuff.
Objects on the heap? they are on the heap. Perhaps you mean on the stack?
Oops! Yes, of course, you're right.
Aug 26 2004
parent Ilya Minkov <minkov cs.tum.edu> writes:
Russ Lewis schrieb:

 Oops!  Yes, of course, you're right.
I had thought that thus the rest of my answer (correctly) assumes you meant what you meant. -eye
Aug 26 2004
prev sibling next sibling parent reply Sai <Sai_member pathlink.com> writes:
I guess the problem is with consistancy,

Either be consistent in syntax for (stack or heap based) memory allocations or
mention it clearly in documentation all possible syntaxes.

We have none right now. Few pages of HTML documentation with scattered
information without search facility is really discouraging.

I have complete faith in Walter, I believe what ever he has decided
on syntax there must be some rationale for it.

So I would be very happy if Walter or some one could dedicate a page
on all possible syntaxes. Being a proactive person, let me start first.

Please correct me if I am wrong, and please add entries of I missed some
syntaxes.

(I am trying to embed HTML in this post)


<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:

<tr>
<td width="54%"><i><b>Syntax</b></i></td>
<td width="21%"><i><b>'var' is reference or 'value' ?</b></i></td>
<td width="28%"><i><b>if reference,<br>
&nbsp;is memory allocated ?</b></i></td>
</tr>
<tr>
<td width="54%"><i><b>For Classes</b></i></td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%">Class_type var;</td>
<td width="21%">reference</td>
<td width="28%">no</td>
</tr>
<tr>
<td width="54%">Class_type var = new Class_type();</td>
<td width="21%">reference</td>
<td width="28%">yes</td>
</tr>
<tr>
<td width="54%">&nbsp;</td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%"><i><b>For Structs</b></i></td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%">Struct_type var;</td>
<td width="21%">value</td>
<td width="28%">-</td>
</tr>
<tr>
<td width="54%">Struct_type* var;</td>
<td width="21%">reference</td>
<td width="28%">no</td>
</tr>
<tr>
<td width="54%">Struct_type* var = new Struct_type;</td>
<td width="21%">reference </td>
<td width="28%">yes</td>
</tr>
<tr>
<td width="54%">&nbsp;</td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%"><i><b>For Arrays</b></i></td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%">Static_array[100] var;</td>
<td width="21%">reference</td>
<td width="28%">yes</td>
</tr>
<tr>
<td width="54%">Dynamic_array[] var;</td>
<td width="21%">reference</td>
<td width="28%">yes</td>
</tr>
<tr>
<td width="54%">Dynamic_array[] var = new Dynamic_array[];&nbsp; </td>
<td width="21%">reference</td>
<td width="28%">yes</td>
</tr>
<tr>
<td width="54%">&nbsp;</td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%"><i><b>For Primitives</b></i></td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
<tr>
<td width="54%">Primitive_type var; </td>
<td width="21%">value</td>
<td width="28%">-</td>
</tr>
<tr>
<td width="54%">Primitive_type* var;</td>
<td width="21%">reference</td>
<td width="28%">no</td>
</tr>
<tr>
<td width="54%">&nbsp;</td>
<td width="21%">&nbsp;</td>
<td width="28%">&nbsp;</td>
</tr>
</table>
Aug 26 2004
parent reply Sai <Sai_member pathlink.com> writes:
I am sorry, the HTML didn't get formatted, may be this whole message
is in <pre> tags.

here is the text version

---------------------------------------------------------------------------------------------
Syntax                                      'var' is reference             if
reference, 
or 'value' ?            is memory allocated ? 

---- For Classes
----------------------------------------------------------------------------

Class_type var;                                reference                      no

Class_type var = new Class_type();             reference
yes 

---- For Structs
----------------------------------------------------------------------------    
Struct_type var;                               value                          - 
Struct_type* var;                              reference                      no

Struct_type* var = new Struct_type;            reference
yes 

---- For Arrays
----------------------------------------------------------------------------    
Static_array[100] var;                         reference
yes 
Dynamic_array[] var;                           reference
yes 
Dynamic_array[] var = new Dynamic_array[];     reference
yes 

---- For Primitives
-------------------------------------------------------------------------     
Primitive_type var;                            value                           -

Primitive_type* var;                           reference
no 

---------------------------------------------------------------------------------------------
Aug 26 2004
parent Sai <Sai_member pathlink.com> writes:
I am sorry again ...... it looks like the lines got truncated
after 80(?) characters on a line.

Here is another version .... looks better and easy to
add more entries. 

---- For Classes ---------------------------------------

Syntax                           : Class_type var; 
Reference or value               : reference  
if reference is memory allocated : no 


Syntax                           : Class_type var = new Class_type();
Reference or value               : reference 
if reference is memory allocated : yes 


---- For Structs ---------------------------------------

Syntax                           : Struct_type var;
Reference or value               : value     
if reference is memory allocated : - 

Syntax                           : Struct_type* var;
Reference or value               : reference
if reference is memory allocated : no 

Syntax                           : Struct_type* var = new Struct_type;
Reference or value               : reference  
if reference is memory allocated : yes

---- For Arrays  ---------------------------------------

Syntax                           : Static_array[100] var; 
Reference or value               : reference 
if reference is memory allocated : yes

Syntax                           : Dynamic_array[] var;
Reference or value               : reference 
if reference is memory allocated : yes 

Syntax                           : Dynamic_array[] var = new Dynamic_array[];
Reference or value               : reference  
if reference is memory allocated : yes 

---- For Primitives ------------------------------------

Syntax                           : Primitive_type var;
Reference or value               : value 
if reference is memory allocated : - 

Syntax                           : Primitive_type* var; 
Reference or value               : reference  
if reference is memory allocated : no 

--------------------------------------------------------
Aug 26 2004
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
i think i'm going to have to agree with russ on this one.  the implicit
extra level of indirection has bitten me in the ass more than a few times,
it's inconsistent, and it seems stupid to have to define 2 versions of a
template, especially if they have virtually no differences.  i'm not so sure
what's so evil about Object* o=new Object.  at least i know it's a pointer.

like you said russ, it would be great if it denied you from creating objects
on the STACK ;)  but still used the pointer syntax.
Aug 28 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Jarrett Billingsley wrote:

 i think i'm going to have to agree with russ on this one.  the implicit
 extra level of indirection has bitten me in the ass more than a few times,
 it's inconsistent, and it seems stupid to have to define 2 versions of a
 template, especially if they have virtually no differences.  i'm not so
 sure
 what's so evil about Object* o=new Object.  at least i know it's a
 pointer.
 
 like you said russ, it would be great if it denied you from creating
 objects
 on the STACK ;)  but still used the pointer syntax.
Why are there two versions? If Foo is a struct and Bar is a class then the following template works fine: template baz(T) { void baz(T x) { x.y = 10; } } baz!(Foo*)(&x1); baz!(Bar)(x2); baz!(Foo)(x3); // this call doesn't do anything interesting, though The beauty is that the "." operator works through references and pointers - a template that just reads x would be able to work with Foo in addition to Foo* but one that changes x needs to have reference semantics. I've always thought it was silly for C++ to have two constructs (struct and class) that mean 99% the same thing and just differ in default visibility. A lost opportunity that Java corrected (well, except they forgot about struct!) -Ben
Aug 28 2004
next sibling parent Mike Swieton <mike swieton.net> writes:
On Sat, 28 Aug 2004 22:12:25 -0400, Ben Hinkle wrote:
 Why are there two versions? If Foo is a struct and Bar is a class then the
 following template works fine:
 template baz(T) {
   void baz(T x) { x.y = 10; }
 }
 baz!(Foo*)(&x1);
 baz!(Bar)(x2);
 baz!(Foo)(x3); // this call doesn't do anything interesting, though
 
 The beauty is that the "." operator works through references and pointers -
 a template that just reads x would be able to work with Foo in addition to
 Foo* but one that changes x needs to have reference semantics.
 
 I've always thought it was silly for C++ to have two constructs (struct and
 class) that mean 99% the same thing and just differ in default visibility.
 A lost opportunity that Java corrected (well, except they forgot about
 struct!)
 
 -Ben
Syntactically, they're the same. You run into problems though, with reference vs. value semantics. Mike Swieton __ Q: That said, do you have any ideas for a really scary reality TV show? A: "C Students from Yale." It would stand your hair on end. - Joel Bleifuss interviewing author Kurt Vonnegut In These Times, 1/27/03
Aug 29 2004
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ben Hinkle wrote:
 Why are there two versions? If Foo is a struct and Bar is a class then the
 following template works fine:
 template baz(T) {
   void baz(T x) { x.y = 10; }
 }
 baz!(Foo*)(&x1);
 baz!(Bar)(x2);
 baz!(Foo)(x3); // this call doesn't do anything interesting, though
 
 The beauty is that the "." operator works through references and pointers -
 a template that just reads x would be able to work with Foo in addition to
 Foo* but one that changes x needs to have reference semantics.
Yes, that template works fine. But what about this one? template baz(T) { void baz(T x) { T temp = x; temp.y = 10; return temp; } } Was that assignment an assignment by reference or value? Has the function modified the value of y in any external object?
Aug 29 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Russ Lewis wrote:

 Ben Hinkle wrote:
 Why are there two versions? If Foo is a struct and Bar is a class then
 the following template works fine:
 template baz(T) {
   void baz(T x) { x.y = 10; }
 }
 baz!(Foo*)(&x1);
 baz!(Bar)(x2);
 baz!(Foo)(x3); // this call doesn't do anything interesting, though
 
 The beauty is that the "." operator works through references and pointers
 - a template that just reads x would be able to work with Foo in addition
 to Foo* but one that changes x needs to have reference semantics.
Yes, that template works fine. But what about this one? template baz(T) { void baz(T x) { T temp = x; temp.y = 10; return temp; } } Was that assignment an assignment by reference or value? Has the function modified the value of y in any external object?
That template also "works fine". I don't think the argument is over what templates do with structs and classes - it is about the fact that structs and classes have different semantics and as a side effect templates behave differently with one or the other. I argue having different semantics is a good thing. Why have two constructs that do the same thing? That is wasteful language design.
Aug 29 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ben Hinkle wrote:
 Russ Lewis wrote:
 
 
Ben Hinkle wrote:

Why are there two versions? If Foo is a struct and Bar is a class then
the following template works fine:
template baz(T) {
  void baz(T x) { x.y = 10; }
}
baz!(Foo*)(&x1);
baz!(Bar)(x2);
baz!(Foo)(x3); // this call doesn't do anything interesting, though

The beauty is that the "." operator works through references and pointers
- a template that just reads x would be able to work with Foo in addition
to Foo* but one that changes x needs to have reference semantics.
Yes, that template works fine. But what about this one? template baz(T) { void baz(T x) { T temp = x; temp.y = 10; return temp; } } Was that assignment an assignment by reference or value? Has the function modified the value of y in any external object?
That template also "works fine". I don't think the argument is over what templates do with structs and classes - it is about the fact that structs and classes have different semantics and as a side effect templates behave differently with one or the other. I argue having different semantics is a good thing. Why have two constructs that do the same thing? That is wasteful language design.
Classes and structs don't do the same thing, and I agree with you that they shouldn't. Structs are low-level constructs. They don't have vtables, they don't have dynamic dispatching, etc. They are pretty much like C structs, and give you the efficiency you sometimes need. Classes, on the other hand, are object oriented: they have dynamic dispatching, inheritance, interfaces, etc. So, assuming that they *are* different, why is it a benefit that, when you declare a pointer to one you use the '*', and when you declare a pointer to the other, you don't?
Aug 30 2004
next sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Russ Lewis schrieb:

 So, assuming that they *are* different, why is it a benefit that, when 
 you declare a pointer to one you use the '*', and when you declare a 
 pointer to the other, you don't?
You don't declare a *pointer* to the other, just that other is a reference type! What is so hard about that? Like, if you pass an array you don't call it "pointer to an array with external length"... This is definately a point of view thing, but i'd say people thinking in "pointer to a class" (instead of "class is a pointer to underlying struct") are just spoiled by the thinking one certain language forces on its users, while *all* of the other languages i'm aware of (Delphi, -eye
Aug 30 2004
parent reply Matthias Becker <Matthias_member pathlink.com> writes:
 So, assuming that they *are* different, why is it a benefit that, when 
 you declare a pointer to one you use the '*', and when you declare a 
 pointer to the other, you don't?
You don't declare a *pointer* to the other, just that other is a reference type! What is so hard about that? Like, if you pass an array you don't call it "pointer to an array with external length"... This is definately a point of view thing, but i'd say people thinking in "pointer to a class" (instead of "class is a pointer to underlying struct") are just spoiled by the thinking one certain language forces on its users, while *all* of the other languages i'm aware of (Delphi,
Perhaps I'm wrong, but that's not the point. We talked about templates which are hard to maintain because the same synthax has different meanings but templates are about writing general code, that can be used everywhere. This thread was only about a suggestion to make the language mor consistent. I don't realy like this idea, but it is still a way to solve the problems that you have when you write template-code. Perhaps we wouldhave needed a different synthax way before. But I think now it's to late for big changes. We could have done something like this: "name" means the value of something, "&name" means the address of something. So to do a value-assignment you write "name1 = name2", to do a reference-assignment you write "&name1 = &name2". On a value-type "&name" is just constant, so you can't assign a new adress, while it's mutable on reference-objects. This way we would have a very consistent notation. ValueType vtFoo, vtBar; ReferenceType rtFoo, rtBar; vtFoo = vtBar; // value-assignemtn &vtFoo = &vtBar; // impossible, &vtFoo isn't an l-value rtFoo = rtBar; // not sure, eigther this isn't allowed or we'd need a opAssign, which would be very constent to havving +=, -=. *=, ... overloadable &rtFoo = &rtBar; // reference-assignment This way the same notation would allways refere to the same thing, so writing template code would be very easy. -- Matthias Becker
Sep 02 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Thu, 2 Sep 2004 14:45:00 +0000 (UTC), Matthias Becker 
<Matthias_member pathlink.com> wrote:
 So, assuming that they *are* different, why is it a benefit that, when
 you declare a pointer to one you use the '*', and when you declare a
 pointer to the other, you don't?
You don't declare a *pointer* to the other, just that other is a reference type! What is so hard about that? Like, if you pass an array you don't call it "pointer to an array with external length"... This is definately a point of view thing, but i'd say people thinking in "pointer to a class" (instead of "class is a pointer to underlying struct") are just spoiled by the thinking one certain language forces on its users, while *all* of the other languages i'm aware of (Delphi,
Perhaps I'm wrong, but that's not the point. We talked about templates which are hard to maintain because the same synthax has different meanings but templates are about writing general code, that can be used everywhere. This thread was only about a suggestion to make the language mor consistent. I don't realy like this idea, but it is still a way to solve the problems that you have when you write template-code.
So you don't think template specialisation solves them? It just occured to me that "specialisation" is almost the opposite of "generic", and D's solution (C/C++ also) to the "generic" problem is to be less generic. Perhaps this is a bad thing, or perhaps it's the natural result of having 'different' types which simply cannot be treated 'generically'. You could wrap all the native types in classes, then you would have a bunch of types you could treat generically. It seems to me that is what is required to enable truly generic programming. That and perhaps a less strict type sort of language. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 02 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <opsdp0visx5a2sq9 digitalmars.com>, Regan Heath says...
Perhaps this is a bad thing, or perhaps it's the natural result of having 
'different' types which simply cannot be treated 'generically'. You could 
wrap all the native types in classes, then you would have a bunch of types 
you could treat generically.
Perhaps we should just all use Smalltalk ;)
It seems to me that is what is required to enable truly generic 
programming. That and perhaps a less strict type sort of language.
I'll admit I'm not entirely comfortable with the reference semantics for class obejcts in D, but I don't know that there's an easy fix on the language side. What I will probably end up doing is use a bunch of little utility templates that are specialized to guarantee expected behavior. However I think classes are the odd man out in this case. Arrays may use references but they have COW semantics, while classes do not. In some respects it would be kind of interesting to have the language support limited COW semantics for classes as well. Perhaps call a copy ctor if a "watched" (ie. non-const) member function is called on a shared object. So you could declare a class this way: Frankly, I'm not sure how useful this would be, but I thought I'd mention it anyway as it's not something I've seen in a language before. Sean
Sep 02 2004
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Sean Kelly wrote:
 I'll admit I'm not entirely comfortable with the reference semantics for class
 obejcts in D, but I don't know that there's an easy fix on the language side.
 What I will probably end up doing is use a bunch of little utility templates
 that are specialized to guarantee expected behavior.
Perhaps a template called ReferenceTo() or PointerTo() which gives the reference type for any given type?
Sep 03 2004
prev sibling next sibling parent Sha Chancellor <schancel pacific.net> writes:
In article <cgvrid$2pic$1 digitaldaemon.com>,
 Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote:

 Classes and structs don't do the same thing, and I agree with you that 
 they shouldn't.  Structs are low-level constructs.  They don't have 
 vtables, they don't have dynamic dispatching, etc.  They are pretty much 
 like C structs, and give you the efficiency you sometimes need. 
 Classes, on the other hand, are object oriented: they have dynamic 
 dispatching, inheritance, interfaces, etc.
 
 So, assuming that they *are* different, why is it a benefit that, when 
 you declare a pointer to one you use the '*', and when you declare a 
 pointer to the other, you don't?
Because one is on the stack, and the other is on the heap. And sometimes it's good to be explicit about what's going on. Everything else that's on the heap requires a *. Even a struct that's on the heap requires a little *. Why not a class? Just because a class CAN'T be on the stack? Phhfftt.
Aug 31 2004
prev sibling parent reply ninjadroid <ninjadroid_member pathlink.com> writes:
I certainly am not qualified to weigh in here, but here goes...

I think that the current D syntax is superior because it will maximize
consistency for the vast majority of uses.  It may seem out of place when you're
still "thinking in C," but I think that the higher level constructs D provides
obviate quite nearly all of the need for pointers.

C pointers serve 2 primary purposes, the first being providing reference
semantics, and the second being providing a means for dynamic memory management.
Taken in this context, the way D considers classes to be a reference type is
indeed confusing, because you must use explicit pointer notation to get the same
effect with other types.

The only exception is with arrays, which have reference semantics themselves.
Now, think back to when you first started programming in C; didn't this confuse
the hell out of you?  It certainly tripped me up, and occasionally catches me
off guard to this day.  So, we should strive to avoid inconsistency in reference
semantics.

With D, reference semantics are provided by a completely different method.  The
in, out, and inout keywords do the job pointers did in C.  So requiring explicit
pointer notation to indicate reference semantics actually introduces an
inconsistency, since the Right Way to do the by-ref dance is *not* with
pointers.[1]

As for memory allocation, D makes pointers unnecessary here as well, although
the reason why isn't quite as obvious.  The most basic dynamic memory need is
resizing an array of objects; the existence of a built in dynamic array type
eliminates the need to create a custom solution here.  

The next step up is with fundamental data structures, such as linked lists,
which need a way of referring to other data containers.  [speculation starts
here]  Now, it seems to me that these things will be best, and typically,
implemented using objects.  Since objects have reference semantics, there is no
need for a pointer here, you can new/delete all you want without ever
encountering a * .

In a practical sense, the only reason you'll ever need to use pointers is
because you're working with structs.  And the only reason you'll ever need to
use structs is because you're doing very low level programming.  If we posit
that people doing such programming know what their doing, then it isn't much a
stretch to assume that they'll be able to deal with the juxtaposition of
implicit and explicit reference semantics.

My $3.50, I hope that all made sense.

[1] This reminds me, is it an error to change the contents of an array that is
passed as an "in" value?  If not, I think it should be, to avoid the kind of
confusion that I mentioned above in regards to C arrays vs. other types.  Same
goes for classes, and maybe even for fundamental types.  It might seem pedantic
to do so, but experienced programmers certainly won't lose anything, and this
"arbitrary roadblock" will help newbies avoid confusion.
Sep 01 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I think that your post is a good summary of many of the great features 
about D.  And in the past, I agreed with you: let's do away with 
pointers in favor of more automatic and safe constructs.  I still think 
that way, at the semantic level.  I'm not suggesting that it should be 
possible to do pointer arithmetic on class references, or anything funky 
like that.

But did you read my original post (the one that started this thread)? 
I've come across a few reasons - that IMHO are fairly compelling - why 
class references should use pointer syntax.  What do you think about 
those reasons?
Sep 01 2004
next sibling parent Regan Heath <regan netwin.co.nz> writes:
On Wed, 01 Sep 2004 10:49:37 -0700, Russ Lewis 
<spamhole-2001-07-16 deming-os.org> wrote:
 I think that your post is a good summary of many of the great features 
 about D.  And in the past, I agreed with you: let's do away with 
 pointers in favor of more automatic and safe constructs.  I still think 
 that way, at the semantic level.  I'm not suggesting that it should be 
 possible to do pointer arithmetic on class references, or anything funky 
 like that.

 But did you read my original post (the one that started this thread)? 
 I've come across a few reasons - that IMHO are fairly compelling - why 
 class references should use pointer syntax.  What do you think about 
 those reasons?
Here are your reasons again, and my thoughts on them:
 1) Templates
 The first one I remember coming across was template code.  Say you want 
 to work with something by reference.  If the thing is a class, then you 
 must use the syntax "T foo" while if it is anything else, you must use 
 the syntax "T* foo".  It would be better if there was consistency.  If 
 there is an '*', then it is a pointer to something.  If there is not, 
 then it is a literal variable.
I agree, this is confusing, annoying and causes subtle bugs. I once changed the type used in a template from a class to a struct and created a bug, that took me 1/2 an hour to find. That said, I *like* the fact that we have 2 different types of object, one value type stack allocated object, and one reference type heap allocated (generally) object. Walter has added the ability for you to specify which types can be used in your templates, so, a well written template does not suffer from this problem any more. I suggested a little while back that modifying an 'in' parameter should be an error, if this were adopted then the compiler would have spotted the bug I created switching from a class to a struct, and most(all?) the bugs created by the differences using struct/class in templates. Basically I think the 2 different types are a good thing, and we can catch the bad things they can potentially cause.
 2) Newbie learning curve
 Many people have posted questions on the newsgroup, asking why their 
 code segfaults when they write code like this:
 	MyClass foo;
 	foo.DoStuff();
I believe the compiler could be improved to spot this obvious error, in which case this is no longer a problem.
 We don't know how many more people just got turned off to D and never 
 asked the question here.  If the '*' were required, then newbies would 
 learn quickly that the right thing to do is:
 	MyClass *foo = new MyClass;
 	foo.DoStuff();
 3) Objects on Heap
 We've had many long discussions about objects on the heap.  The fact 
 that D lacked them lead to 'auto' variables, and still people want more. 
   Frankly, I think that objects on the heap make a lot of sense 
 sometimes;  we could have them and still avoid the copy constructor 
 stuff.
We already have objects on the heap, via structs and 'auto' classes, and they work as you'd want them to work, I see no problem here. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 01 2004
prev sibling parent reply ninjadroid <ninjadroid_member pathlink.com> writes:
In article <ch523h$2etg$2 digitaldaemon.com>, Russ Lewis says...
But did you read my original post (the one that started this thread)? 
I've come across a few reasons - that IMHO are fairly compelling - why 
class references should use pointer syntax.  What do you think about 
those reasons?
Good call, I'm sorry about that. Let me rectify this situation: 1) Templates I don't understand the practical implications of the problem you point out. The fact that objects have implicit reference semantics and structs don't doesn't seem to cause any tangible woes. I think that if the distinction was indeed problematic, it would have been discovered long ago in C++ since arrays and char/int/etc. have a similar distinction. Can you post a link to an example that demonstrates this problem? Length isn't an issue. Regardless, even if there are situations where the different semantics cause a problem in the context of templates, I'd wager that they are very few in number, and infinitesimal in a relative sense. Now that D can specify which types are allowable in a template, such exceptional circumstances can be handled. It's obviously not a perfect method, but I believe it is optimal. It's best to make those scenarios which represent 99% of use cases easier at the expense of the 1% fringe than vice versa. (Although it does frost my cherries when I happen to find myself in that 1% :-) 2) Newbie learning curve The compiler should flag this as an error, plain and simple. On a similar note, I've been giving this some more thought, and I really think that having the 'in' keyword designate strict enforcement (changing the variable is an error) is The Right Thing. That, combined with encouraging new programmers to *always* specify in, out, or inout should mask the confusion of implicit/explicit reference semantics completely. If the in keyword is omitted, then things can behave as usual to prevent the frustration of experienced users. 3) Objects on the [Stack] I think the combination of the 'auto' keyword and garbage collection obviate the need for this; there simply isn't any benefit.
Sep 02 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Thu, 2 Sep 2004 16:06:06 +0000 (UTC), ninjadroid 
<ninjadroid_member pathlink.com> wrote:
 In article <ch523h$2etg$2 digitaldaemon.com>, Russ Lewis says...
 1) Templates

 I don't understand the practical implications of the problem you point 
 out.  The fact that objects have implicit reference semantics and 
 structs don't doesn't seem to cause any tangible woes.  I think that if 
 the distinction was indeed problematic, it would have been discovered 
 long ago in C++ since arrays and char/int/etc. have a similar 
 distinction.  Can you post a link to an example that demonstrates this 
 problem?  Length isn't an issue.
Here is a simplified example showing what I personally encountered: template t(T) { void change(T a) { a.prop++; } } class A { int prop; } struct B { int prop; } void main() { A a = new A(); B b; //a.prop == 0 t!(A).change(a); //a.prop now == 1 //b.prop == 0 t!(B).change(b); //b.prop now == 0; } of course this is simplified and it's easy to see what is happening, the real case was much more complicated. That said, I think template specialisation solves this problem eg. template t(T : class) { void change(T a) { a.prop++; } } does the above allow you to use it with other 'reference' types eg the built in arrays? if not perhaps a 'value' or 'reference' keyword in template specialisation would be nice eg. template t(T : reference) { void change(T a) { a.prop++; } } Another solution is to change the template to template t(T) { void change(inout T a) { a.prop++; } } in other words pass the struct by reference. I believe checking 'in' parameters are not modified would have spotted this bug for me, I still advocate this change. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 02 2004
parent ninjadroid <ninjadroid_member pathlink.com> writes:
In article <opsdpz09y05a2sq9 digitalmars.com>, Regan Heath says...
Another solution is to change the template to

template t(T) {
   void change(inout T a) {
     a.prop++;
   }
}

in other words pass the struct by reference.

I believe checking 'in' parameters are not modified would have spotted 
this bug for me, I still advocate this change.
That strikes me as the closest you're gonna get to The Right Thing. I also think it solidifies the case for strictly enforcing in parameters! Is there any reason not to?
Sep 02 2004