Three Unlikely Successful Features of D

Nick Sabalausky a at a.a
Wed Mar 21 01:01:52 PDT 2012


"H. S. Teoh" <hsteoh at quickfur.ath.cx> wrote in message 
news:mailman.949.1332305140.4860.digitalmars-d at puremagic.com...
>
> Yeah, AA's with int keys are like arrays enhanced with O(1)
> insertion/removal and sparse storage (if you have very large indices,
> e.g.). :-) You can even have (pseudo) linear access if you iterate keys
> from 0 to $.

Exactly.

> For example, it lets you accept keys/values that are not strictly the
> AA's key/value type, but can be implicitly converted to them. It lets
> you return keys and values without needing the ugly typeinfo and void*
> casts that are necessary in aaA.d.  This in turn lets you mark many AA
> methods as pure, and almost all as @safe or @trusted. It lets you
> cleanly interoperate with types that define opAssign (currently aaA.d
> does a blind binary copy of data from key/value pointers, which leads to
> potential bugs when the data has subobjects.)
>
> It also makes it *much* easier to fix many existing AA bugs in the
> bugtracker. So far, I have working unittests for the following issues:
> 3824, 3825, 4337, 4463, 5685, 6210, 7512, 7512, 7602, 7632, 7665, 7665,
> 7704. I haven't looked through all AA-related issues yet; this list may
> very well grow. :-) To fix these in the current aaA.d implementation can
> be rather tricky, and quite possibly requires compiler changes.
>

I see. Cool stuff.

> Better yet, I thought of a way of making AA's instantiable at
> compile-time via CTFE and mixins: this will let you write AA literals
> that can be evaluated at compile-time and have them turn into object
> code directly without needing runtime initialization.
>

Ah! Now *that's* fucking awesome! That limitation has kinda stuck out as an 
odd, annoying wart.

>
>> And even *that* still doesn't work if you don't catch *every*
>> exception (and then rethrow the ones you don't care about? Ick!).
>
> Actually, you can catch "..." and it will catch *everything*. And I
> believe a single "throw;" will rethrow whatever it is you caught.
>

I see. It has been awhile since I've been in the C++ loop.

>
>> I've seen C++ programmers swear off exceptions because of this, and I
>> can't blame them at all.  Exception systems *need* a finally.
>
> Yeah. "catch(...)" sorta works, but it's very ugly. And while being able
> to throw *anything* at all is nice (I'm guilty of writing code that
> throws char*, for example), not being able to make *any* assumptions at
> all about what you caught (e.g., no common exception superclass with
> some useful methods, like .msg) is, shall we say, practically useless in
> a large enough project?
>

Boy, it really has been awhile. I knew you could throw any class object in 
C++, but I thought that was all. Haxe lets you throw literally anything, 
even Int, and I found that to be more of a problem than a feature (In very 
much the same way as VB's ability to change the lower bound of an array: It 
gains little and just means you have to remember to do the rediculous 
"UBound(arr) - LBound(arr)".)  Just have a proper Exception class and 
nothing else should be throwable (D's "Error" notwithstanding).

> (Actually, the lack of .msg was what drove me to throw char*. Obviously
> checking return codes for every lousy function I call is out of the
> question, but so is throwing error codes that come from different
> subsystems, since you've no way of telling which error code scheme to
> use to look up the error. So I said to myself, why not throw a string
> that actually tells you what the error is? Furthermore, if these strings
> were predefined in char arrays that had unique pointer addresses, the
> value of the pointer itself serves as a kind of "global error number".
> So this worked as a kind of a poor man's error code + message exception
> that can be freely thrown around without problems

That is an interesting work around.

> the reason I shied
> away from throwing class objects in the first place was because early
> implementations of C++ had problems that sometimes caused pointer bugs
> and all kinds of nasty side effects when a class object is thrown.

Ouch!

>
> Scope guards rule. Ironically, D's GC mostly alleviates the need for
> scope guards. :-) They're still immensely useful when you acquire
> resources that must be cleaned up no matter what happens later. D is the
> first and only language I know that got resource cleanup done right.
> Cleanups belong with the acquisition code, not dangling somewhere 200
> lines down at the end of the scope, with who knows how many possible
> leaks in between due to goto's, exceptions, returns, and who knows what!
>

I've even come across at least a couple uses of scope guard that aren't 
strictly related to releasing resources. Actually, I've just been working 
with both of them today:

- Temporarily changing a value:

    int bar;
    void foo()
    {
        // Change bar temporarily
        auto saveBar = bar;
        bar = 777;
        scope(exit) bar = saveBar;

        [...do anything here...]
    }

- Timing a section of code. I saw this trick in someone else's code once, 
loved it, and made a helper tool out of it:

https://bitbucket.org/Abscissa/semitwistdtools/src/4455e019cd95/src/semitwist/util/mixins.d#cl-643

It's a string mixin that, after features and hygenic issues and such, 
basically boils down to mixing in this psuedo-code:

    if(verbose)
    {
        write(customMessage, "..."); // no newline
        stdout.flush();
        startTimer(); // Using the awesome new std.datetime
    }
    scope(exit) if(verbose)
        writeln(timerInMilliseconds(), "ms");

>
> CTFE even makes it possible to express what many recursive templates
> express, in pure imperative style. I mean, you can't get any better than
> this:
>
> int factorial(int n) {
> int result = 1;
> while (n>1) {
> result *= n;
> result--;
> }
> return result;
> }
> enum x = factorial(12); // compile-time computation
> int y = factorial(12); // runtime computation
>
> In C++, you'd have to use recursive templates that are extremely
> difficult to read and write beyond the simplest of functions.
>

Yup exactly. It's Haskell with bad syntax and compile times. Not only that, 
but even something as trivial as "if" has to be built out of pure-fp 
primitives in C++-template-land, whereas D just has: "static if...done!"

Of course, to C++'s credit, its templates (AIUI) weren't really designed for 
metaprogramming, just for generics. The metaprogramming was just a happy 
accident (At least that's my understanding, maybe I'm wrong...)

>
>> With C++'s templates, it would appear that you have to use recursion
>> and helper templates for damn near anything.
> [...]
>
> Not to mention the horrible, horrible, syntax that comes with recursive
> templates. My previous manager used to tell me that as soon as he sees
> nested templates deeper than 2 levels, his eyes start glazing over, and
> it all becomes just arcane black magic.
>

Yea. Even a "simpler" use of C++'s templates can feel like staring into the 
depths of hell: Ever see a loop over a collection using STL iterators? 
Sheesh. I like the idea behind the STL, but with syntax like that I'd rather 
use the old "for(int i=0; i<theEnd; i++)". Gimme a proper foreach anyday.

Come to think of it, foreach probably fits the bill for the OP: For people 
who came from C/C++ like I did, it can seem like a relatively trivial sugar 
over "for(int i=0; i<theEnd; i++)". But it makes such a difference to just 
not have to constantly deal with those minute mechanics.

> -- 
> Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich 
> Schubert

lol :) Now that's without a doubt one of my favorites.




More information about the Digitalmars-d mailing list