Jump to page: 1 2 3
Thread overview
struct vs. class, int vs. char.
Apr 28, 2009
MLT
Apr 28, 2009
bearophile
Apr 28, 2009
MLT
Apr 28, 2009
bearophile
Apr 28, 2009
grauzone
Apr 28, 2009
Denis Koroskin
Apr 28, 2009
grauzone
Apr 28, 2009
Denis Koroskin
Apr 29, 2009
Daniel Keep
Apr 29, 2009
Denis Koroskin
Apr 29, 2009
Denis Koroskin
Apr 29, 2009
Denis Koroskin
Apr 28, 2009
Denis Koroskin
Apr 28, 2009
grauzone
Apr 29, 2009
Don
Apr 29, 2009
Georg Wrede
April 28, 2009
D is really an amazing language! It really is everything that C++ did not manage to be, at least as far as my limited experience with it shows.

I noticed a couple of "problems" with it, and wanted to comment on them. It could easily be that these comments just stem from my ignorance.

1. struct vs. class
As far as I understand, these two reserved words, that in C++ mean very similar things, in D are very different. A class variable is "really" a pointer, whereas a struct is directly allocated memory.

What I don't like is that it seems that structs and classes should almost be interchangeable - one might implement something as a class and later want it to be a struct, or vice versa. It almost is actually a local decision. I might want something to be a class in one place, and a struct in another.

And, it seems that struct and class refer to several different things: (1) struct and class are allocated in different places, (2) struct is supposed to ensure its structure, and (3) struct is copied by value, whereas class by reference.
It seems to me that these are 3 different issues, and one should have control over them separately.

I find this example a bit strange:
struct S { int x;}
class C {int x;}
void main() {
{
  S x ;
  x.x = 1 ;
  S y = x ;
  y.x =2 ;
  writefln(x.x) ; // x.x is 1
}
{
  C x = new C ;
  x.x = 1;
  C y = x ;
  y.x = 2 ;
  writefln(x.x) ; // x.x is 2
}
}

I would have preferred to declare directly that the variable y takes or doesn't take a reference when assigned. Otherwise, it seems to me that a program that uses both (struct and class) can get mightily confusing.
Maybe the solution is to not use struct (which is I guess what is recommended unless you really need it.)


2. char[] vs. int[]
I think it is strange that
char[] x = "1234" ;
x[0] = '4' ;

Produces a run time error, but
int[] x = [1,2,3,4] ;
x[0] = 4 ;
Doesn't. I think that they both should, or both shouldn't - to be consistent (and it would be better if they both didn't). Best would be again, to allow the programmer to specify where the array (or other stuff) should be stored.

3. Builtin types vs. classes
(Here I probably am just missing the right phobos reference). D's builtin structures are very rich. Associative arrays with arbitrary keys! Given that, it is even sadder than it is in C++, that one can not inherit from the basic types. I want to implement something that is basically an associative array of one of my classes, with additional features. To do that, I will probably have to put the associative array as a component, and redefine all components/operations, or inherit from an associative array  in a library...


April 28, 2009
Welcome.

MLT:
> D is really an amazing language!<

Yep.


>It really is everything that C++ did not manage to be, at least as far as my limited experience with it shows.<

I also hope D will never be lot of the things C++ is, because C++ is too many things :-)


> 1. struct vs. class As far as I understand, these two reserved words, that in C++ mean very similar things, in D are very different.<

C++ classes are like structs, but their methods are private by default.
In D structs have value semantics, are copied by value and keep the given order of the fields, while classes have reference semantics, are often allocated on the heap, and the compiler in theory can change the order of fields in memory.
They are designed for different purposes, and I agree with such decision by Walter. C# has chosen in a similar way.
But later practice has shown that you want struct constructors, and other things, so now D2 has them too. So structs are a bit closer to classes now in D2.


>A class variable is "really" a pointer, whereas a struct is directly allocated memory.<

A class is a "reference". There are some ways manual or automatic with "scope" to sometimes allocate classes on the stack to improve performance. I have seen that sometimes it gives a nice speedup. You can't create an array of scoped classes by normal means, you have to manually change the way they allocate memory. Generally you can do what you want, but you may need some work to do special kinds of memory allocation.



>What I don't like is that it seems that structs and classes should almost be interchangeable - one might implement something as a class and later want it to be a struct, or vice versa. It almost is actually a local decision. I might want something to be a class in one place, and a struct in another.<

In D they are by design different, because most of the times you want just one of the meanings.


>It seems to me that these are 3 different issues, and one should have control over them separately.<

Giving control over each thing is of course nice and it may even increase performance in special situations. But it surely leads to an increase of the complexity of the language (and its use). D tries to be less complex than C++ (and C++ today is probably one of the most complex languages, and such very high complexity is now slowly killing it), and this means that sometimes you have to give up on some flexibility. If the language is well designed then most of the times, in practical programs, you don't feel much of such limitations.


>Otherwise, it seems to me that a program that uses both (struct and class) can get mightily confusing.<

It's confusing if you are used to program in a language like Python, where everything acts according to a reference semantics (more or less, it's not exactly like that, it's a name-semantics). But D is more complex and powerful than Python, and lower level too, and it gives you both reference and data semantics. Once you know that, you just have to keep in memory that classes are by reference and structs are by values, and you can live well (you can also have pointer to structs. I think I have never used a pointer to class in D yet).


> Maybe the solution is to not use struct (which is I guess what is recommended unless you really need it.)<

Structs are useful in D because classes have some overhead in both memory, and their methods are always virtual (and all the current D compilers are unable to devirtualize such methods, maybe not even GDC is able to do it, even if G++ is sometime able to devirtualize C++ virtual methods, I think it's the only C++ compiler able to do that)), and being usually allocated on the heap (D compilers aren't like HotSpot that is sometimes able to allocate classes on the stack when it is sure they not exit the scope) they aren't fit if you for example need small [x,y] vectors.


> 2. char[] vs. int[]
> I think it is strange that
> char[] x = "1234" ;
> x[0] = '4' ;
> 
> Produces a run time error, but
> int[] x = [1,2,3,4] ;
> x[0] = 4 ;
> Doesn't. I think that they both should, or both shouldn't - to be consistent (and it would be better if they both didn't).

I leave this to other people. in D2 strings are immutable, by the way, while static arrays are not.


>Best would be again, to allow the programmer to specify where the array (or other stuff) should be stored.<

That requires you to pay some complexity. And D designers/programmers may be unwilling to pay it.


>Given that, it is even sadder than it is in C++, that one can not inherit from the basic types.<

Yes, this is a problem of D2. Scala language shows that there are better ways to design types, as you say. Walter has recently changed the D2 language to allow a bit of this, using "this". With that you can define a struct that behaves for example like the built-in associative array, and you don't need to redefine all its almost-methods.

Bye,
bearophile
April 28, 2009
On Tue, 28 Apr 2009 13:00:36 -0400, bearophile <bearophileHUGS@lycos.com> wrote:

>> MLT:
>> 2. char[] vs. int[]
>> I think it is strange that
>> char[] x = "1234" ;
>> x[0] = '4' ;
>>
>> Produces a run time error, but
>> int[] x = [1,2,3,4] ;
>> x[0] = 4 ;
>> Doesn't. I think that they both should, or both shouldn't - to be consistent (and it would be better if they both didn't).
>
> I leave this to other people. in D2 strings are immutable, by the way, while static arrays are not.

This has been discussed.  Most people agree that the behavior should be consistent.  Any array literal should be exactly that -- an unchangable literal.  In D2, it should be an immutable literal (and produce a compile time error if you try and change it).

BTW, this behavior you noticed is only on certain OSes.  On other OSes (I think windows), you get weird behavior:

char[] x = "1234";
x[0] = '4'; // no runtime error
char[] y = "1234"; // y actually now equals "4234"!

-Steve
April 28, 2009
bearophile wrote:
> Yes, this is a problem of D2. Scala language shows that there are
> better ways to design types, as you say.

Could you please give a little more detail?

And speaking of Scala, after having watched a presentation, I was left with the impression that traits are nothing but classes with parameterized base.

I mean this:

abstract class Person {
  def schedule:Schedule
}

trait Student extends Person {
  private var classSchedule:Schedule = ...

  override def schedule = classSchedule

  def learn() = {...}
}

trait Worker extends Person {
  private var workSchedule:Schedule = ...

  override def schedule = workSchedule

  def work() = {...}
}

class CollegeStudent(school:School, company:Company) extends Student with Worker {
  // ...
}

is really this (in Scala-D made-up language):

abstract class Person {
  def schedule:Schedule
}

class Student(Base) extends Person {
  private var classSchedule:Schedule = ...

  override def schedule = classSchedule

  def learn() = {...}
}

class Worker(Base) extends Person {
  private var workSchedule:Schedule = ...

  override def schedule = workSchedule

  def work() = {...}
}

class CollegeStudent(school:School, company:Company) extends Worker!(Student!(CollegeStudent)) {
  // ...
}

Is that correct?


Andrei
April 28, 2009
On Tue, Apr 28, 2009 at 12:07 PM, MLT <none@anon.com> wrote:

> What I don't like is that it seems that structs and classes should almost be interchangeable - one might implement something as a class and later want it to be a struct, or vice versa. It almost is actually a local decision. I might want something to be a class in one place, and a struct in another.

I hear this all the time from C++ users.  But in practice, it virtually never comes up.  I have never, in my five years of using D, wanted to change a class to a struct or vice versa, or ever seen anyone else doing that (or complaining about its difficulty).  The only people who complain are those who don't use the language ;)

> And, it seems that struct and class refer to several different things: (1) struct and class are allocated in different places, (2) struct is supposed to ensure its structure, and (3) struct is copied by value, whereas class by reference.
> It seems to me that these are 3 different issues, and one should have control over them separately.

Again, in practice, you will virtually always want *either* value semantics *or* polymorphism.  In the few cases where you need both, you can imitate it with mixins and the like (and with D2, 'alias this' is nice).

> I find this example a bit strange:
> struct S { int x;}
> class C {int x;}
> void main() {
> {
>  S x ;
>  x.x = 1 ;
>  S y = x ;
>  y.x =2 ;
>  writefln(x.x) ; // x.x is 1
> }
> {
>  C x = new C ;
>  x.x = 1;
>  C y = x ;
>  y.x = 2 ;
>  writefln(x.x) ; // x.x is 2
> }
> }
>
> I would have preferred to declare directly that the variable y takes or doesn't take a reference when assigned. Otherwise, it seems to me that a program that uses both (struct and class) can get mightily confusing.

Again, in practice, it doesn't :)  You always use some types as values and others always as references.  Are C programs that use both ints and char* s mighty confusing?

> Maybe the solution is to not use struct (which is I guess what is recommended unless you really need it.)

Please don't make everything a class.  This isn't Java ;) What is instead recommended is that you use structs for value types and classes for everything else.
April 28, 2009
Jarrett Billingsley wrote:
> On Tue, Apr 28, 2009 at 12:07 PM, MLT <none@anon.com> wrote:
> 
>> What I don't like is that it seems that structs and classes should almost be interchangeable - one might implement something as a class and later want it to be a struct, or vice versa. It almost is actually a local decision. I might want something to be a class in one place, and a struct in another.
> 
> I hear this all the time from C++ users.  But in practice, it
> virtually never comes up.  I have never, in my five years of using D,
> wanted to change a class to a struct or vice versa, or ever seen
> anyone else doing that (or complaining about its difficulty).  The
> only people who complain are those who don't use the language ;)


Often I'd wish to statically allocate an object in the current context, like you can do with a struct. scope partially gives me what I want. It would be nice if "scope" could be used in other places:

class Bla {
    int member;
}

class Foo {
   scope Bla b; //object constructed here
}

static assert(Foo.classinfo.init.size == 5*4);

Yeah, you'd need a solution for how to call the ctor. Maybe the (very annoying) way of how initialization of final/const members is handled would be useful here.
April 28, 2009
Steven Schveighoffer Wrote:

> BTW, this behavior you noticed is only on certain OSes.  On other OSes (I think windows), you get weird behavior:
> 
> char[] x = "1234";
> x[0] = '4'; // no runtime error
> char[] y = "1234"; // y actually now equals "4234"!
> 
> -Steve

Pretty nice. It would be even nicer if you had the following:

char[] x = "1234";
x[0] = '4'; // no runtime error
char[] y = "12"; // y now equals "42"

;)


April 28, 2009
On Tue, 28 Apr 2009 21:42:51 +0400, grauzone <none@example.net> wrote:

> Jarrett Billingsley wrote:
>> On Tue, Apr 28, 2009 at 12:07 PM, MLT <none@anon.com> wrote:
>>
>>> What I don't like is that it seems that structs and classes should almost be interchangeable - one might implement something as a class and later want it to be a struct, or vice versa. It almost is actually a local decision. I might want something to be a class in one place, and a struct in another.
>>  I hear this all the time from C++ users.  But in practice, it
>> virtually never comes up.  I have never, in my five years of using D,
>> wanted to change a class to a struct or vice versa, or ever seen
>> anyone else doing that (or complaining about its difficulty).  The
>> only people who complain are those who don't use the language ;)
>
>
> Often I'd wish to statically allocate an object in the current context, like you can do with a struct. scope partially gives me what I want. It would be nice if "scope" could be used in other places:
>
> class Bla {
>      int member;
> }
>
> class Foo {
>     scope Bla b; //object constructed here
> }
>
> static assert(Foo.classinfo.init.size == 5*4);
>
> Yeah, you'd need a solution for how to call the ctor. Maybe the (very annoying) way of how initialization of final/const members is handled would be useful here.

I belive it could be implemented as a library type, using Scope(T) template:

class Foo
{
    Scope!(Bar) _bar;
}

Perphaps, Phobos could provide that functionality, then we could deprecate "scope Foo foo = new Foo(args);" in favor of "auto foo = Scope!(Foo)(args);" and eventually remove from language.
April 28, 2009
On Tue, Apr 28, 2009 at 6:07 PM, MLT <none@anon.com> wrote:
> 2. char[] vs. int[]
> I think it is strange that
> char[] x = "1234" ;
> x[0] = '4' ;
>
> Produces a run time error, but
> int[] x = [1,2,3,4] ;
> x[0] = 4 ;
> Doesn't. I think that they both should, or both shouldn't - to be consistent (and it would be better if they both didn't). Best would be again, to allow the programmer to specify where the array (or other stuff) should be stored.
>

It pretty much boils down to this (mostly said in other replies)

* string literals are special, they are allocated in a static data segment, readonly if the platform allows it.

* arrayliterals as non-static expressions are always heap allocated. even when there's absolute no need for it... (see http://d.puremagic.com/issues/show_bug.cgi?id=2356 )

-Tomas
April 28, 2009
> I belive it could be implemented as a library type, using Scope(T) template:

I don't like that approach to replace random language features by templates at all. And in this case, you really have to ask WHY?

It's nice that it's possible, but there are some reasons that speak against this approach:
1. it's harder to understand in general (even if the language definition becomes smaller)
2. error messages turn into an incomprehensible mess
3. puts more stress on the compiler and it gets slower (you will understand when you have to _wait_ for your project to finish compilation, even if you use the superfast dmd)

> class Foo
> {
>     Scope!(Bar) _bar;
> }

Would typeof(_bar) == Bar?

> Perphaps, Phobos could provide that functionality, then we could deprecate "scope Foo foo = new Foo(args);" in favor of "auto foo = Scope!(Foo)(args);" and eventually remove from language.
« First   ‹ Prev
1 2 3