Walter Bright:

>I'd rephrase that as D supports many different styles. One of those styles is 
>as a "better C".<

D can replace many but not all usages of C; think about programming an Arduino 
(http://en.wikipedia.org/wiki/Arduino ) with a dmd compiler of today.


>I agree on those points. Those features would not be used when using D as a 
>"better C".<

A problem is that some of those D features can worsen a kernel code. So for 
example you have to review code to avoid operator overloading usage :-)
There is lot of D compiler complexity useless for that kind of code. A simpler 
compiler means less bugs and less D manual to read.


>The answer is that C++ doesn't offer much over C that does not involve those 
>trouble causing features. D, on the other hand, offers substantial and 
>valuable features not available in C or C++ that can be highly useful for 
>kernel dev. Read on.<

I don't know if D offers enough of what a kernel developer needs.


>A non-standard feature means the language is inadequate.<

I agree, standard C is not perfect for that purpose.


>There is nothing at all preventing non-standard features from being added to D 
>for specific tasks. There is no reason to believe it is harder to do that for 
>D than to C.<

I agree. (But note that here we are talking just about low level features. 
Linus has said that such features are important but he desires other things 
absent in C).


>As for standard features D has that make it more suitable for low level 
>programming than C is:<

I agree.


>Since it has more than C does, and C is used for kernel dev, then it must be 
>enough.<

Kernel C code uses several GCC extensions to the C language. And Linus says he 
desires higher level features absent from C, C++ and absent from those GCC 
extensions.


>I'll await your reply there.<

I appreciate your trust, but don't expect me to be able to teach you things 
about C and the kind of code needed to write a kernel, you have way more 
experience than me :-)

--------------------

>With all due respect to Linus, in 30 years of professionally writing software, 
>I've found that if you solely base improvements on what customers ask for, all 
>you have are incremental improvements. No quantum leaps, no paradigm shifts, 
>no game changers.<

You are right in general, but I don't know how much you are right regarding 
Linus.
Linus desires some higher level features but maybe he doesn't exactly know what 
he desires :-)
I don't know if Linus has ever asked for some of the features of the Sing# 
language (http://en.wikipedia.org/wiki/Sing_Sharp ), needed to write the 
experimental Singularity OS.


About Spec#:

>The Spec# language is a superset of the programming language C# extending C# 
>by nonnull types, method contracts, object invariants and an ownership type 
>system [and Spec# also has built-in message passing for concurrency with a 
>syntax to specify message invariants]. The behavior of a Spec# program is 
>checked at runtime and statically verified by Boogie, the Spec# static program 
>verifier [2]. Boogie generates logical verification conditions from a Spec# 
>program. Internally, it uses an automatic theorem prover [7] that analyzes the 
>verification conditions to prove the correctness of the program or find errors 
>in it. One of the main innovations of Boogie is a systematic way (a 
>methodology) for specifying and verifying invariants. The Spec# Programming 
>System handles callbacks and aggregate objects, and it supports both object 
>[4] and static [3] class invariants.<


In Spec# beside the "assert" there is also "assume", it seems similar to this 
one of C++:
http://msdn.microsoft.com/en-us/library/1b3fsfxw%28VS.80%29.aspx
But Spec# "assume" seems used mostly for the contract programming, for example 
to state that some condition is true before some method call that has that 
thing as precondition. I have not fully understood the purpose of this, but I 
think it can be useful for performance (because contracts are enforces in 
"release mode" too. So the compiler has to try to remove some of them to 
improve code performance).


In Spec# nonnull types are specified adding "!" after their type:
T! t = new T(); // OK
t = null; // not allowed

Even if D can't turn all its class references to nonnull on default, a syntax 
to specify references and pointers that can't be null can be added. The bang 
symbol can't be used in D for that purpose, it has enough purposes already.


Spec# defines three types of purity:
- [Pure] Method does not change the existing objects (but it may create and 
update new objects).
- [Confined] Method is pure and reads only this and objects owned by this.
- [StateIndependent] Method does not read the heap at all.
Add one of the three attributes above to a method to declare it as pure method. 
Any called method in a contract has to be pure.


Spec# "static class invariants" test the consistency of static fields.
http://research.microsoft.com/en-us/projects/specsharp/krml153.pdf

>Sometimes there are even consistency conditions that relate the instance 
>fields of many or all objects of a class; static class invariants describe 
>these relations, too, since they cannot be enforced by any one object in 
>isolation.<
This is an example, written in Pseudo-D:


class Client {
    int id;
    static int last_used_id = 0;

    static invariant() {
        assert(Client.last_used_id >= 0);

        HashSet!int used_ids;
        foreach (c; all Client instances) {
            assert(c.id < Client.last_used_id);
            assert(c.id !in usef_ids);
            usef_ids.add(c.id)
        }
    }
    
    this() {
        this.id = Client.last_used_id;
        Client.last_used_id++;
    }
}

>Every object of class Client has an ID. The next available ID is stored in the 
>static field last_used_id. Static class invariants guarantee that last_used_id 
>has not been assigned to a Client object and that all Client objects have 
>different IDs.<


In D class/struct invariants can access static fields too. Findind all 
instances of a class is not immediate in D, I don't think D reflection is 
enough here, you have to store all such references, for example in a static 
array of Client.


So in D it can become something like:

class Client {
    int id;
    static int last_used_id = 0;
    static typeof(id)[] clients;

    invariant() {
        assert(Client.last_used_id >= 0);

        // assert len(set(c.id for c in clients)) == len(clients)
        // assert all(c.id < Client.last_used_id for c in clients)

        HashSet!int used_ids;
        foreach (c; clients) {
            assert(c.id < Client.last_used_id);
            assert(c.id !in usef_ids);
            usef_ids.add(c.id)
        }
    }

    this() {
        this.id = Client.last_used_id;
        Client.last_used_id++;
        clients ~= this;
    }
}


But running that invariant often is slow. I don't know if/how Spec# solves this 
problem.

--------------------

> It's interesting that D already has most of the gcc extensions:
> http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html

There's lot of stuff in that page, and some of those things are new for me :-)

4.3 Labels as Values: that's computed gotos, they can be useful if you write an 
interpreter or you implement some kind of state machine. In the last two years 
I have found two situations where I have found useful this feature of GCC. I'd 
like computed gotos in D too (both GDC and LDC can implement them in a simple 
enough way. If this is hard to implement with the DMD back-end then I'd like 
this feature to be in the D specs anyway, so other D compilers that want to 
implement it will implement it with the same standard syntax, improving 
portability of D code that uses it).

I will write about more of those GCC things tomorrow...

Bye,
bearophile

Reply via email to