The D language

Started by
90 comments, last by Nitage 17 years, 10 months ago
Quote:Original post by Anthony Serrano
Quote:Original post by DeadXorAlive
How about a regular expressions compiler? At the end of the following article you can find the source code:
templates revisited


Quote:Original post by h3r3tic
How about you take a look at the examples I posted ?


Congratulations for completely failing to understand my point. I'm not looking for examples of D being better/simpler/more whatever than C++, and if I was, I know where to find them. I'm pointing out to Drakex that his attempt at showing D's alleged superiority failed miserably, and offered a suggestion for future attempts.


I liked to think this thread will not result in a c++ vs d flame. I understood your point very well.

Quote:
Quote:
So are you saying you can finally(after 10-15 years) do the following in C++?
string a = "Hello " + " World";

No, I'm saying that strings are conceptually different from arrays, and therefore any language that insists that a string is just an array of char/wchar/dchar is not markedly better than C++ in that regard. ( And if you don't believe me about string!=array, then tell me this: should {1, 2, 3} + {4, 5, 6} equal {1, 2, 3, 4, 5, 6} or {5, 7, 9}? )

D deals with the difference between concatenation and addition by having different operators for that.
Hmm maybe this is my lack of knowledge, but how are strings different from arrays? As far as I can tell, a string is a subset of array: an (dynamic) array of characters.
Somehow i get the idea you are thinking of c / c++ arrays when talking about arrays in D. Arrays in D are more like vectors in C++, which is just an array but not broken.

Advertisement
Quote:
In terms of syntactical aesthetics they're both ugly and about as ugly as each other.


Cpp requires ~20% more code and twice as many files for the same D equivilent. Most of Cpp's problem is it keeps all its cruft from C to be source compatible. Translate any large C++ project to D to see what I mean. I don't know if you even looked at the C++ to D comparison? ( http://www.digitalmars.com/d/cpptod.html )

Look at this C++ code

struct A{	int operator <  (int i);	int operator <= (int i);	int operator >  (int i);	int operator >= (int i);};int operator <  (int i, A &a) { return a >  i; }int operator <= (int i, A &a) { return a >= i; }int operator >  (int i, A &a) { return a <  i; }int operator >= (int i, A &a) { return a <= i; } 


and compare it to the D equivilent

struct A{	int opCmp(int i); }


Cpp is death by 1000 papercuts multiplied by the size of your project.

Quote:
Preprocessor macros aren't necessarily ugly


Show me a beatiful Macro then, as all the ones I have seen are ugly. They are a language within a language and are very bug prone.



In all fairness, in order to more fully implement the C++ example, the above D example really should be:
struct A {    int opCmp   (int i);    int opCmp_r (int i);}


Otherwise you miss the cases of (i < a) which C++'s global overload handled. But the essential point stands firm.
Christopher Nicholson-SaulsD Programming Language | Mango Tree
Quote:Original post by Anthony Serrano
re you trying to say that it's harder to figure out that a( b ) resolves to a.operator()( b ) (C++) than it is to figure out that a >= b resolves to ( a.opCmp( b ) )>=0 (D)?


One of the motivating factors behind D's operator naming scheme is clear intent. The problem with the C++ style syntax is that the symbols themselves do not give any indication of what the operator does. Is the + operator addition or concatenation? Is the vector * vector operator a component-wise scalar multiplication or a dot product (I have seen code doing both)?

opAdd always means add, and never means concatanate. opMul always means multiply, and never means dot product. Of course, it's still possible for people to make the methods do something different, but in that case they are breaking a contract. When operator overlaods are named with a symbol, the implementor can define that symbol to mean anything he wants it to. Your array example below is exactly the problem D's operator overload syntax is meant to solve.

Quote:No, I'm saying that strings are conceptually different from arrays, and therefore any language that insists that a string is just an array of char/wchar/dchar is not markedly better than C++ in that regard. ( And if you don't believe me about string!=array, then tell me this: should {1, 2, 3} + {4, 5, 6} equal {1, 2, 3, 4, 5, 6} or {5, 7, 9}? )


I don't see that strings are conceptually different. They are sequences of characters. What is an array if not a sequence of items? In D, your example is not a problem, since D has a specific concatenation operator that cannot be confused with addition:

char[] str1 = "Hello ";char[] str2 = str1 ~ "World";


So if I wanted to create a sequence container or some such in D that had array semantics, allowed the addition of a value to all items in the sequence and allowed concatenation of arrays or other sequence containers, I can do so without any confusion. Here's a contrived example, an array of 3D Vectors:

struct Vec3f{   float x, y, z;      void opAddAssign(Vec3f) { ... }   ...}class VecArray{public:   ...   // copy constructor   this(VecArray va)   {      this._vecs = va._vecs.dup;   }   // Add a vector to all elements in the array (vecArray += vec)   void opAddAssign(Vec3f rhs)   {      foreach(Vec3f v; _vecs)          v += rhs;   }   // Add a vector to a copy of the array (sum = vecArray + vec)   VecArray opAdd(Vec3f rhs)    {      VecArray copy = new VecArray(this);      copy += rhs;      return copy;   }   // Append a vector to the end of the array (vecArray ~ vec)   void opCat(Vec3f v)   {      _vecs ~= v;   }   // You could also append in reverse, though I wouldn't (vec ~ vecArray)   void opCat_r(Vec3f v)   {      _vecs ~= v;   }   // Return the vector at index i   Vec3f opIndex(int i) { return _vecs; }     // Assign a vector to index i   void opIndexAssign(Vec3f v, int i) { _vecs = v; }    // You could also do this (Vec3f[] threeVecs = vecArray[0,4,3]))   void opIndex(int i, int j, int k) { ... }   // you can assign to multiple indices also (vecArray[1, 3, 5] = vec)   void opIndex(Vec3f, int i, int j, int k) { ... }   // slicing   Vec3f[] opSlice() { return _vecs[]; }   Vec3f[] opSlice(int begin, int end) { return _vecs[begin..end]; }      void opSliceAssign(Vec3f[] v, int begin, int end) { _vecs[begin..end] = v; }   void opSliceAssign(Vec3f[] v) { _vecs[] = v; }private:   Vec3f[] _vecs;}


No confusion here. In C++, one person might use operator+ to add and operator+= to concatenate, while another might use operator+= to add-assign and have a separate concatenation method. You would have no way to know which is which just by the interface.
Quote:Original post by ibisbasenji
In all fairness, in order to more fully implement the C++ example, the above D example really should be:
*** Source Snippet Removed ***

Otherwise you miss the cases of (i < a) which C++'s global overload handled. But the essential point stands firm.


Not so! You only need "opCmp". Because it is defined as a commutative operator, it's smart enough to try both forms.

For any overloaded operator in D, the syntax "a op b" tries to find:

a.opfunc(b)
b.opfunc_r(a)

And then, if the operator is commutative and neither of these is found, it tries the reverse:

a.opfunc_r(b)
b.opfunc(a)

So given the following trivial example:

import std.stdio;struct A {    int n;    int opCmp(int m) { return n - m; }}void main() {    static A a = { 10 };    writefln("a &gt; 5: %s", a &gt; 5);    writefln("5 &gt; a: %s", 5 &gt; a);}


... everyting Just Works. In this case, saying "5 > a" falls through to the last case (it calls a.opCmp(int) ), and it's even smart enough to flip the comparison operator around so the math works out.
I think I would have much preferred the # to symbolize string concatenation than ~. In C, I believe ## was a preprocessor string concatenator, so # would have been a more natural transition. ~ makes me think of flipping bits.

Beginner in Game Development?  Read here. And read here.

 

This is what I've come across so far in my (brief) usage of D:

Problems with D:
Operator overloading is problematic:
- IMO the rational for NaN being part of the language instead of the library is covering for the fact that D's comparsion operator can't express the NaN relationship ( ie NaN > NaN, NaN == NaN and NaN < NaN are all false). The language also doesn't account for the fact that many object have a sensible equality relationship, but no sensible greater than or less than relationship
- The lack of an overloadable assignment operator prevents correctly managing resources - the rational being that reference counting etc. isn't required in a language with garbage collection - this rational fails to account for the fact that programs may wish to manage resources other than memory
- The lack of an overloadable . or -> operator makes it impossible to create any kind of smart pointer or handle object. Again, the language has forgotten to account for managing non-memory resources.

No CV-qualifiers:
- I don't use volatile in C++, so I can't really comment on that.
- I find the lack of const to be very weird - D is designed to be better than C++ at expressing constraints and to avoid long winded workarounds, yet due to the lack of const, C++ wins hands down over D in this area.

Core Language overload:
- Too many constructs get special treatment because they are in the core language. This prevents libraries from integrating cleanly into the language, as they don't get the same perks

Lack of 3rd party libraries:
- can't compete with C++ on the amount of libraires/utilities available

That's quite a small list - my C++ irritations list would be much longer (but then I'm much more experienced with C++).
Quote:Original post by Aldacron
One of the motivating factors behind D's operator naming scheme is clear intent. The problem with the C++ style syntax is that the symbols themselves do not give any indication of what the operator does. Is the + operator addition or concatenation? Is the vector * vector operator a component-wise scalar multiplication or a dot product (I have seen code doing both)?

opAdd always means add, and never means concatanate. opMul always means multiply, and never means dot product. Of course, it's still possible for people to make the methods do something different, but in that case they are breaking a contract. When operator overlaods are named with a symbol, the implementor can define that symbol to mean anything he wants it to. Your array example below is exactly the problem D's operator overload syntax is meant to solve.

Quote:No, I'm saying that strings are conceptually different from arrays, and therefore any language that insists that a string is just an array of char/wchar/dchar is not markedly better than C++ in that regard. ( And if you don't believe me about string!=array, then tell me this: should {1, 2, 3} + {4, 5, 6} equal {1, 2, 3, 4, 5, 6} or {5, 7, 9}? )


I don't see that strings are conceptually different. They are sequences of characters. What is an array if not a sequence of items? In D, your example is not a problem, since D has a specific concatenation operator that cannot be confused with addition:

*** Source Snippet Removed ***

So if I wanted to create a sequence container or some such in D that had array semantics, allowed the addition of a value to all items in the sequence and allowed concatenation of arrays or other sequence containers, I can do so without any confusion. Here's a contrived example, an array of 3D Vectors:

*** Source Snippet Removed ***

No confusion here. In C++, one person might use operator+ to add and operator+= to concatenate, while another might use operator+= to add-assign and have a separate concatenation method. You would have no way to know which is which just by the interface.


And if a person can't guess, it's even harder for a computer to figure out. One of the motivations behind design choices is to make D programs fairly easily parseable, for making compilers and reverse by a UML tool relatively easy for example. So the syntax must not be ambiguous. C++ choices are deplorable in this matter, and makes metaprogramming and code parsing extremely difficult.
Quote:Original post by h3r3tic
D currently supports implicit function template instantiation for non-member functions and will be getting full support soon. The comparison sheet just hadn't been upated when Walter added IFTI to D.
http://www.digitalmars.com/d/template.html

The changelog for DMD.149 states "Added limited support for implicit function template instantiation.". /Limited/ - only for non-members.


In D overloading operators can only be member functions so you still can not write/build/use expression templates and C++ style DSELs until D adds full-support. Either that or D provides a different method of support for writing efficient DSELs.

Quote:Original post by h3r3tic
Even without IFTI, D's templates are more powerful than these in C++. If you don't buy it, just try them. Looking at the specs will not tell you much if you dismiss D outright.


I'm not doubting they are but i'm not interested in languages with templates that are turing complete. If i where to design a language which is a truely better alternative to C++ in all aspects D is not what i have in mind.

Why reintroduce another ad-hoc type system & templates? Parametric polymorphism, ad-hoc polymorphism and metaprogramming should all be separate things (but operate together smoothly) not some intertwined monstrousity that is C++ & D templates. The fact that D got rid of include files and the single-phase processing limitation yet still uses templates for parametric polymorphism i find odd, C++ has no choice but to use templates to implement parametric polymorphism.

You can do better than turing complete templates, a truely better alternative to C++ would be a language which has a formalized type system. It would be a dependently typed language with a formal effects system which also has support for compile & runtime multi-staged programming that should work with the type system seemlessly. This would cover virtually all the things what advance C++ programmers try to actually do in C++.

Quote:Original post by h3r3tic
What's so special about boost::fusion ? A library with functionality similar to boost::python is underway, I've got a lib that accomplishes what boost::bind does. So you say that boost::fusion is the next logical step ? Thanks, I'll make sure D makes it as well.


From what you've just told me you can only write a subset of boost::fusion, also boost::fusion works seemlessly with boost mpl (which is one of the points of the library's existance) and hence the multi-stagged programming i mentioned earlier.

There isn't an equivalent of boost mpl in D (as of yet, as far as i know) and before you (or anyone else) tell me D doesn't need such a library because it has more powerful template system then you're missing the other points of boost mpl which is not just about getting over limitations of C++ templates so you must consider that in mind.

Thats not to say D couldn't have such a library but you need somebody with knowledge of a non-strict purely functional programming language & functional programming techniques aswell as templates to do it. This mostly goes with boost::fusion aswell so are you sure you are ready to do it in D?

If you want to know what boost::fusion is about you can read it for yourself here.

Quote:Original post by clayasaurus
Cpp requires ~20% more code and twice as many files for the same D equivilent. Most of Cpp's problem is it keeps all its cruft from C to be source compatible. Translate any large C++ project to D to see what I mean.


You've misunderstood completely, what does productivity have to do with syntactical aesthetics really? not much (if at all). Regardless of that C++ and D are both syntactically ugly period, are you trying to say that all languages which are C-derived or C-dialects are more syntactically pleasing than say haskell?

Quote:Original post by clayasaurus
Show me a beatiful Macro then, as all the ones I have seen are ugly.


Did i say they where beautiful? did i not mention it's nothing like having a real macro system? did you even bother to read (carefully) everything i wrote?

If you can not see they're value when it comes to eliminating repetitive boilerplate(meta)code as to what can happen in doing lots of template metaprogramming such as (partial) specializations then i can't help you.

Quote:
They are a language within a language and are very bug prone.


Oh so virtually the entire of boost libraries must be very buggy then. Do you have something against domain specific embedded languages? and i'm not suggesting one would write one with C/C++ preprocessor macros either.

[Edited by - snk_kid on July 1, 2006 7:20:28 AM]
Quote:Original post by Nitage
This is what I've come across so far in my (brief) usage of D:

Problems with D:
Operator overloading is problematic:
- IMO the rational for NaN being part of the language instead of the library is covering for the fact that D's comparsion operator can't express the NaN relationship ( ie NaN > NaN, NaN == NaN and NaN < NaN are all false). The language also doesn't account for the fact that many object have a sensible equality relationship, but no sensible greater than or less than relationship


It does the equality != greater than or less than thing, you can have opEquals() and opCmp().

Quote:
- The lack of an overloadable assignment operator prevents correctly managing resources - the rational being that reference counting etc. isn't required in a language with garbage collection - this rational fails to account for the fact that programs may wish to manage resources other than memory
- The lack of an overloadable . or -> operator makes it impossible to create any kind of smart pointer or handle object. Again, the language has forgotten to account for managing non-memory resources.

Lack of overloadable assigment operator doesn't prevent implementing reference counting. Nobody says GC will solves all the problems. I do miss a good way to have raii, there is an raii (confusingly named auto) type in D with deterministic destruction but it is too limited imho. I miss the raii from C++ sometimes.
Quote:
No CV-qualifiers:
- I don't use volatile in C++, so I can't really comment on that.
- I find the lack of const to be very weird - D is designed to be better than C++ at expressing constraints and to avoid long winded workarounds, yet due to the lack of const, C++ wins hands down over D in this area.

Yes, the rationale for not having C++ like const was that it really doesn't guarantee anything in C++, Walter Bright's position is that it's not much better than documentation and there is in, out and inout for function parameters. There is some talk about changing this to have const the default ( here is an exchange between Alexandrescu and Bright: link ). I agree this is still better in C++, I hope this will be in D and const will be the default.

Quote:
Core Language overload:
- Too many constructs get special treatment because they are in the core language. This prevents libraries from integrating cleanly into the language, as they don't get the same perks

You're talking about the data structures?
Quote:
Lack of 3rd party libraries:
- can't compete with C++ on the amount of libraires/utilities available

Indeed. Although one tool I like very much in D, build, I haven't seen anything like this for C++.

This topic is closed to new replies.

Advertisement