www.digitalmars.com         C & C++   DMDScript  

D - Language design... and implementation

reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
   I guess I should start by apologizing to Walter for starting in this
forum in "harsh critic mode". I guess he probably gets that a lot. Probably
a "fair" punishment for going ahead and do what most of us would like to do
but don't, namely, designing (and implementing, for Chrissakes!) his own
language and compiler.

   So from here, I'd like to commend his work. It's awesome to say the
least.

   Nevetheless, there's always opinions and opinions, so I'll keep pusing
for mine. If you want me to stop, Walter, then tell me.

   Ok... So... From the online documentation...

 Functions: "There is no inline keyword. The compiler makes the decision

Hmmm... My inclination is exactly the opposite. What I'd want is a "macro" keyword that enforces the inlining of a function. I want to be able to tell the compiler to inline it, and fail with an error if for any reason it can't.
 Classes, Constructors: "The vptr initialization is performed before the

I suppose that you're aware that this by itself makes constructors behave very differently from the C++ standard. I actually like it this way no less than what the C++ does. It brings me back to my times with Turbo Pascal :) Still, it should be a BIG emphatical point in the "Programming in D for C++ Programmers" section.
 Asserts:

Asserts are a very powerful thing. I believe that the decission of making them a primitive is a very good one. Still, it would be even better to formalize compile-time asserts. Those are asserts where the asserted expression is constant. It would be a good thing to force compilers to evaluate such asserts at compile-time
 Contracts: "If a function in a derived class overrides a function in its

satisified" I'm not sure this is advisable. The "in" contract of a derived method should be forced to be implied by the contract for the parent method. For example: --- class A { int[10] nums; int Convert(int k) in { assert(k >= 0 && k < 10); } body { return nums[k]; } } class B: A { int Convert(int k) in { assert(k >= 0 && k < 12); } body { printf("Converting %d\n", k); return super.Convert(k); } } --- This allows you to do: --- B b = new b; b.Convert(11); --- Which would blow. The derived class has broken the contract of its parent. I agree that it shouldn't happen, but then the compiler should help with enforcing it. Instead, a somewhat acceptable alternative would be to use only the derived's contract when calling, and have the parent's contract be used only if/when the parent implementation is called. Salutaciones, JCAB
Jan 18 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:a2acct$bi$1 digitaldaemon.com...

 Functions: "There is no inline keyword. The compiler makes the decision

Hmmm... My inclination is exactly the opposite. What I'd want is a "macro" keyword that enforces the inlining of a function. I want to be

 to tell the compiler to inline it, and fail with an error if for any

 it can't.

Why exactly do you need it? From your POV there is no difference other than perfomance, and you can be sure that compiler does his best to optimize it... inline functions aren't C macro, anyhow...
 Classes, Constructors: "The vptr initialization is performed before the

I suppose that you're aware that this by itself makes constructors

 very differently from the C++ standard. I actually like it this way no

 than what the C++ does. It brings me back to my times with Turbo Pascal :)
 Still, it should be a BIG emphatical point in the "Programming in D for

 Programmers" section.

What is great in it is that constructor in base classes can call virtual methods of child classes. This was impossible to do in C++. Also, constructors can call each other this way - so no need to make a separate initialization function, a common thing in C++.
    Asserts are a very powerful thing. I believe that the decission of

 them a primitive is a very good one. Still, it would be even better to
 formalize compile-time asserts. Those are asserts where the asserted
 expression is constant. It would be a good thing to force compilers to
 evaluate such asserts at compile-time

I woudln't be surprised if there's already something like that in D.
    I'm not sure this is advisable. The "in" contract of a derived method
 should be forced to be implied by the contract for the parent method. For

In-contracts are what _function_ expects. Since function in derived class might be different of that in base class, providing its own implementation, it _can_ cover an extended range of values (remember, deriving from a class means _extending_ this class!). This can be demonstrated with this example: class A { void func(int x) in { assert(x > 0 && x < 10); } { ... } } class B: A { override void func(int x) in { assert(x > 0 && x < 20); } { ... } } A obj = new B; obj.func(5); // since B is child of A, it must be able // to do at least what A can... obj.func(15); // ...but might do even more! Programmer is responsible for calling super() with appropriate arguments. If his function doesn't, it breaks the contract with super() - but not the user who called that function!
Jan 18 2002
next sibling parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a2afi4$2hh$1 digitaldaemon.com...
 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:a2acct$bi$1 digitaldaemon.com...

    Hmmm... My inclination is exactly the opposite. What I'd want is a
 "macro" keyword that enforces the inlining of a function. I want to be

 to tell the compiler to inline it, and fail with an error if for any

 it can't.

Why exactly do you need it? From your POV there is no difference other than perfomance, and you can be sure that compiler does his best to optimize it... inline functions aren't C macro, anyhow...

"and you can be sure that compiler does his best". Hmmm... No. I want to do things like put in functions for accessible properties, (which D does, for example), and still be 100% assured that there's no performance penalty. Even in debug mode. I don't want to give the compiler the choice.
 Classes, Constructors: "The vptr initialization is performed before



 constructor is ever called (by the new operator)"

What is great in it is that constructor in base classes can call virtual methods of child classes. This was impossible to do in C++.

:) Yes.
 Also, constructors can call each other this way - so no need to make
 a separate initialization function, a common thing in C++.

Exactly. The downside is that it loses the strictness of C++ as far as the "life" of objects is concerned, which is a good thing, even if it gets in the way sometimes.
    Asserts are a very powerful thing. I believe that the decission of

 them a primitive is a very good one. Still, it would be even better to
 formalize compile-time asserts. Those are asserts where the asserted
 expression is constant. It would be a good thing to force compilers to
 evaluate such asserts at compile-time

I woudln't be surprised if there's already something like that in D.

:) It wouldn't see its potential until some form of parametric polymorphism (templates) made it into the language. But it'd shine then. Imagine being able to add an error message to the assert. I'd buy into the language, despite my differences of opinion with Walter, just to get that.
    I'm not sure this is advisable. The "in" contract of a derived method
 should be forced to be implied by the contract for the parent method.


 In-contracts are what _function_ expects. Since function in derived
 class might be different of that in base class, providing its own
 implementation, it _can_ cover an extended range of values (remember,
 deriving from a class means _extending_ this class!). This can be
 demonstrated with this example:

 [...]

     obj.func(5);    // since B is child of A, it must be able
                     // to do at least what A can...

     obj.func(15);   // ...but might do even more!

But it still could do less and get away with it! Until it crashes! What I'm talking is about the compiler (or the runtime, in this case) _enforcing_ that. Salutaciones, JCAB
Jan 18 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:a2aguf$3hf$1 digitaldaemon.com...

    I want to do things like put in functions for accessible properties,
 (which D does, for example), and still be 100% assured that there's no
 performance penalty. Even in debug mode. I don't want to give the compiler
 the choice.

I guess D is a wrong tool for this. BTW why do you want "no perfomance penalty" in debug mode? It was never supposed to run fast! Just remember about DBC, invariants etc...
    Exactly. The downside is that it loses the strictness of  C++ as far as
 the "life" of objects is concerned, which is a good thing, even if it gets
 in the way sometimes.

I don't see any good way to combine this power with strictness of C++. If I have to choose, I choose the D way.
    But it still could do less and get away with it! Until it crashes! What
 I'm talking is about the compiler (or the runtime, in this case)

 that.

How can this be implemented? Remember, in-blocks don't only contain simple asserts - they can have loops (see string.toStringz() for an example of such), and are in general just another sort of subroutines. You can run one then another (as it's done with out-blocks), but how do you check if it provides _at least_ (and not _at most_) the same "range" as its superclass?
Jan 18 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a2aifk$4ja$1 digitaldaemon.com...
 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:a2aguf$3hf$1 digitaldaemon.com...

    I want to do things like put in functions for accessible properties,
 (which D does, for example), and still be 100% assured that there's no
 performance penalty. Even in debug mode. I don't want to give the


 the choice.

I guess D is a wrong tool for this. BTW why do you want "no perfomance penalty" in debug mode? It was never supposed to run fast! Just remember about DBC, invariants etc...

I develop games. I often run in debug mode because it's easier to debug (duh! ;). The problem is that I also want to use high-level abstractions that don't change the semantics of my program. Macros are perfect for that. And, AFAIKS, there's absolutely no technical reason why the debugger (MSVC's for example) couldn't single-step inlined functions (macros) on their original source. It's one of those things that make me miserable sometimes. Remember: debug doesn't necessarily have to mean "as slow as possible". Just "slow enough to be easily debuggeable" would be enough.
    Exactly. The downside is that it loses the strictness of  C++ as far


 the "life" of objects is concerned, which is a good thing, even if it


 in the way sometimes.

I don't see any good way to combine this power with strictness of C++. If I have to choose, I choose the D way.

:) That's fair enough. Personally, I'd have to play with it a while before I'll buy into that.
    But it still could do less and get away with it! Until it crashes!


 I'm talking is about the compiler (or the runtime, in this case)

 that.

How can this be implemented? Remember, in-blocks don't only contain simple asserts - they can have loops (see string.toStringz() for an example of such), and are in general just another sort of subroutines.

Yes. That baffles me. :) Still...
 You can run one then another (as it's done with out-blocks), but how
 do you check if it provides _at least_ (and not _at most_) the same
 "range" as its superclass?

You can't, you're right. The other option is what I said before: that only the actually called method's contract is the valid one, and that calling through "super" invokes super's contract instead. That is definitely doable by the compiler, and it swould be safe. Salutaciones, JCAB
Jan 18 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
news:a2ak4n$5mq$1 digitaldaemon.com...

    Remember: debug doesn't necessarily have to mean "as slow as possible".
 Just "slow enough to be easily debuggeable" would be enough.

...which gives an opportunity for D to make functions inline even in debug build, if it can then handle them properly.
    You can't, you're right. The other option is what I said before: that
 only the actually called method's contract is the valid one, and that
 calling through "super" invokes super's contract instead. That is

 doable by the compiler, and it swould be safe.

Invoking parent contract with super() - I like the idea!
Jan 18 2002
parent "Juan Carlos Arevalo Baeza" <jcab roningames.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a2alq1$6ik$1 digitaldaemon.com...
 "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message
 news:a2ak4n$5mq$1 digitaldaemon.com...

    Remember: debug doesn't necessarily have to mean "as slow as


 Just "slow enough to be easily debuggeable" would be enough.

...which gives an opportunity for D to make functions inline even in debug build, if it can then handle them properly.

I admit it... I'm a "control freak" of sorts. :) Still... if I intend for something to have no impact on performance, I'm the one who knows it, not the compiler, so I _should_ be able to express it. I'd like the compiler to tell me if it has a problem with that. On MSVC, we have the "__forceinline" extension, but MSVC will use only if it feels like it, as it stands. Intel's compiler will respect it very well, but it's a generally poorer compiler in my experience. Also, sometimes, it's hard for the compiler to inline a chain of 10 function calls, even if the result would be a single simple assembler instruction. If they haven't been able to get it right, I doubt the D compiler will. Sincerely. It'd make me warmer at night just to know I can get a compiler error when things are not done the way I intended them. This happens a lot when making functions that just redirect the call elsewhere (to "super", or to another aggregated class, for example). This brings me to another interesting point: function redirections. Having to make a whole new function with body just have it call a similar function in an aggregated object seems like a good place to improve the user-friendliness of the language. Salutaciones, JCAB
Jan 18 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a2afi4$2hh$1 digitaldaemon.com...
 Also, constructors can call each other this way - so no need to make
 a separate initialization function, a common thing in C++.

Yes! One thing in C++ that caused me incessant bugs was a class with multiple constructors. I'd add a new member as a routine improvement, and forget to put the initializer for it into one of the constructors. With D, you can have the more complex constructors call a "default" constructor, which sets all the members. Alternatively, the default for a member can be set in the member declaration itself. Worst case, the member is automatically initialized by a default set by the type.
Jan 19 2002