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