October 25, 2012
On Wednesday, October 24, 2012 13:31:14 Walter Bright wrote:
> The default compare for structs is a bit compare of the contents.

Which definitely seems inherently broken. Doing that only works if the struct
only contains integral types, character types, or bool. Nothing else will
compare properly that way. It really needs to work like arrays do (or are
supposed to anyway) and compare each member according to == and only fallback
to an outright bitwise compare when it knows that the results would be
identical (i.e. because all of the members are integral types, character
types, bool, or other structs which hold only integral types, character types,
or bool - be it directly or in other structs that they hold). Bitwise comparison
is the is operator's job, not ==.

- Jonathan M Davis
October 25, 2012
On Thu, Oct 25, 2012 at 04:08:21AM +0200, Era Scarecrow wrote: [...]
> I'll just hope my data processing won't require anything that's broken or going to be depreciated, I've already dropped using std.stream for ranges instead. Dropping classes in favor of structs.

Huh? Why would you drop classes in favor of structs? They are used for two different purposes. Classes are for when you need polymorphism: inheritance, overloading, interfaces, all the trappings of OO programming. Structs are for when you need value types that doesn't need OO-style manipulations. Not the same thing.


T

-- 
One Word to write them all, One Access to find them, One Excel to count them all, And thus to Windows bind them. -- Mike Champion
October 25, 2012
On Wed, Oct 24, 2012 at 10:12:48PM -0400, Jonathan M Davis wrote:
> On Wednesday, October 24, 2012 13:31:14 Walter Bright wrote:
> > The default compare for structs is a bit compare of the contents.
> 
> Which definitely seems inherently broken. Doing that only works if the struct only contains integral types, character types, or bool. Nothing else will compare properly that way. It really needs to work like arrays do (or are supposed to anyway) and compare each member according to == and only fallback to an outright bitwise compare when it knows that the results would be identical (i.e. because all of the members are integral types, character types, bool, or other structs which hold only integral types, character types, or bool - be it directly or in other structs that they hold). Bitwise comparison is the is operator's job, not ==.
[...]

The built-in AA's suffer from the same problem. Which is why sometimes, if you use a struct as key, entries cannot be found because the key being looked up is bitwise different from any key in the AA, but they have the same value (e.g., the struct has a reference to two distinct objects that have the same value).


T

-- 
Food and laptops don't mix.
October 25, 2012
On Thursday, 25 October 2012 at 02:19:39 UTC, H. S. Teoh wrote:

> Huh? Why would you drop classes in favor of structs? They are used for two different purposes. Classes are for when you need polymorphism: inheritance, overloading, interfaces, all the trappings of OO programming. Structs are for when you need value types that doesn't need OO-style manipulations. Not the same thing.

 Indeed. Instead of interfaces you'd use templates, OO doesn't change really all that much. There's also large amounts of memory consumption  that just isn't needed for data that hasn't and/or isn't going to change. More often I'm thinking structs w/templates are more powerful than classes.

 I'm experimenting with a polymorphic struct. Run-time price is a couple enum checks if there's multiple functions that might qualify, and slightly different calling for those polymorphic types.
October 25, 2012
On Wednesday, October 24, 2012 21:01:11 Andrei Alexandrescu wrote:
> OK let's stop this. There's a bug that needs fixing, is all.

And as bad as things may be now, they're _way_ better than they were a couple of years ago (or even a year ago). The situation isn't perfect, but it's constantly improving. It's becoming rarer and rarer for programmers to run into compiler bugs. You can actually write plenty of programmers now without running into any, whereas before, you'd run into them all the time.

- Jonathan M Davis
October 25, 2012
On Thursday, 25 October 2012 at 02:55:20 UTC, Jonathan M Davis wrote:
> On Wednesday, October 24, 2012 21:01:11 Andrei Alexandrescu wrote:
>> OK let's stop this. There's a bug that needs fixing, is all.
>
> And as bad as things may be now, they're _way_ better than they were a couple of years ago (or even a year ago). The situation isn't perfect, but it's constantly improving. It's becoming rarer and rarer for programmers to run into compiler bugs. You can actually write plenty of programmers now without running into any, whereas before, you'd run into them all the time.
>
> - Jonathan M Davis


Yeah that's definitely very true. :) Sorry that the negative feedback seems to drown out anything that looks positive... but I've definitely noticed the improvement (and that's what makes me come back and try writing a program in D every once in a while). Props to you guys for the work!
October 25, 2012
On Thu, Oct 25, 2012 at 04:46:04AM +0200, Era Scarecrow wrote:
> On Thursday, 25 October 2012 at 02:19:39 UTC, H. S. Teoh wrote:
> 
> >Huh? Why would you drop classes in favor of structs? They are used for two different purposes. Classes are for when you need polymorphism: inheritance, overloading, interfaces, all the trappings of OO programming. Structs are for when you need value types that doesn't need OO-style manipulations. Not the same thing.
> 
>  Indeed. Instead of interfaces you'd use templates, OO doesn't change
>  really all that much. There's also large amounts of memory
>  consumption  that just isn't needed for data that hasn't and/or isn't
>  going to change. More often I'm thinking structs w/templates are more
>  powerful than classes.
[...]

Used correctly, *classes* with templates can be pretty powerful too.

Recently I wrote an expression parser that can either parse the expression into a tree, or compute its value on-the-fly. I didn't like the idea of duplicating the parser code for both usages, so I decided to turn the parser into a template that would do one or the other. The template would call a static class method at key points during the parse, which alleviates the parser from knowing (or caring) what was done with the parsed expression -- the method could build a tree, or evaluate the expression, etc..

While I was coding that, I realized that lots of code is still being duplicated -- for example, to implement building a tree with +, -, *, and / operators _and_ to evaluate the same operators on-the-fly, required that operator-specific code be written in both classes. That was when I realized that I can leverage inheritance by factoring the operator-specific code into the base class, and overriding the static method in the derived class so that when the parser was instantiated with the base class, it built an expression tree, whereas when instantiated with the derived class, it would evaluate the expression. And moreover, the return type of the parser would be the derived class, so the caller doesn't even need to downcast to read the computed value. And all the while, the derived class can also be used as leaf nodes in the full expression tree, when building the latter.

So even though things like templated virtual methods don't work, you *can* still make pretty potent combinations of templates + classes.


T

-- 
Doubtless it is a good thing to have an open mind, but a truly open mind should be open at both ends, like the food-pipe, with the capacity for excretion as well as absorption. -- Northrop Frye
October 25, 2012
Funny, I was actually building a (GLR) parser, too.

Keeping track of duplicate item sets was what tripped me up.
October 25, 2012
On Thu, Oct 25, 2012 at 05:34:05AM +0200, Mehrdad wrote:
> Funny, I was actually building a (GLR) parser, too.
> 
> Keeping track of duplicate item sets was what tripped me up.

Oh. You should've said so earlier -- for such specific applications, you don't _need_ a fully generalized solution to the AA hash problem. You can just implement your own .toHash methods on the structs/objects your parser uses, and be done with it. No need to try to accomodate every single type D has or can have -- that's the kind of thing Phobos developers have to worry about, not people who have a specific application in mind.

I've learnt from hard experience that premature generalization is just as evil as premature optimization. (Believe me, I'm a sucker for completely general solutions too -- I like killing ants with nuclear warheads, proverbially speaking, just for the feeling of confidence that that it would've also worked in the hypothetical scenario of levelling an entire city.) You usually end up completely frustrated that the system/language you're using just doesn't have that one last bit of homogeneity for your generalization to fully work, and eventually the project never gets off the ground. But in many cases, I've learned, this is all needless pain. Write the case-specific code first, and then refactor and generalize it afterwards. Works much better, I find, and you have a tangible product to speak of from the get-go, instead of spending countless hours writing a generic framework that eventually never gets used. Plus, when you actually have a *working* codebase, exactly which generalizations will work, and which are just castles in the air, tend to be a lot more obvious than when you're just sketching the product on the drawing board.

Just my slightly more than $0.02. :)


T

-- 
English has the lovely word "defenestrate", meaning "to execute by throwing someone out a window", or more recently "to remove Windows from a computer and replace it with something useful". :-) -- John Cowan
October 25, 2012
Haha thanks a bunch for the advice. :) I was actually intending it to be a quick hack, not a highly-generalized thing.

So I thought, hey, I'll just use a
Tuple!(string, q{symbol}, size_t, q{iRule}, size_t, q{iSymbol})
to denote an "LR Item".



So an "item set" became RedBlackTree!(Tuple!(...)), but that didn't work because of the reference semantics.

So I tried int[Tuple!(...)], and hence the story.



(I actually even scrapped the idea of using a 'set' and just used an array.

Screw the O(n) performance.

But one of my goals was getting the code to be concise (the idea was to use CTFE + mixins/templates to generate some optimized code with switch statements for the parser), but sprinkling it with all the sort()s and all the code required to switch from hashtables to arrays just made it too ugly/indecipherable for me to continue working on it.)

etc.

But yeah, thanks for the advice, I'll keep it in mind. :)