April 28, 2012
On Saturday, 28 April 2012 at 21:02:25 UTC, H. S. Teoh wrote:
> [...]
> I disagree. Having a dedicated foreach construct allows the compiler to
> optimize away the delegate in certain cases. I wouldn't want to incur
> the cost of creating and passing a delegate in something as simple as
> foreach (i; 0..100), for example.
>
>
compiler inlining?
There is no reason why this should be special cased for a specific redundant construct in the language. Shouldn't I enjoy similar optimization techniques when using other kinds of functions with delegate parameters?

>> * enum - enum should be completely redesigned to only implement
>> what it's named after: enumerations.
>
> Actually, I rather like the enum idiom of declaring compile-time
> constants. Though it could do with a renaming to something more
> befitting.
>
>
>> * version - this does not belong in a programming language. Git
>> is a much better solution.
>
> This is an interesting idea. But using separate git branches just for
> having versioned code seems a bit like total overkill... plus a
> maintenance nightmare since you have to continue pull and merge changes
> to every porting branch every time development happens. Whereas having
> everything represented in source means that whoever writes a new feature
> is also responsible for making it work with whatever versions are
> currently out there. After-the-fact fixes are always painful.
>
>
>> * di files - a library should encapsulate all the info required
>> to use it. Java Jars, .Net assemblies and even old school; Pascal
>> units all solved this long ago.
>
> I proposed a while ago that .di files should be replaced by something
> better: omit ALL function bodies, template bodies, private members,
> etc., and just keep the "real" public API in the human-readable part of
> the file. Function and template bodies should be kept in as a binary
> blob readable by the compiler (which obviously needs to know them
> otherwise it won't be able to expand templates).
>
> (Yes the binary blob can be reverse-engineered, but so can executables,
> so it's a moot point. We're not trying to write cryptographic security
> here, but it's nice to separate what the compiler needs to know vs. what
> the user of a library needs to know.)
>
>

I agree with the general notion here. Whatever the actual implementation details are, the API should be strongly tied to the binary in order to insure consistency and ease of use. I shouldn't need to worry if the header files match the binary library. Regarding the human readable API - that's why we have documentation for.

>> * This is a big one: get rid of *all* current compile time
>> special syntax. It should be replaced by a standard compilation
>> API and the compiler should be able to use plugins/addons. This
>> would reduce the size of the language to half of its current
>> size, maybe even more.
>
> I have to disagree here. CTFE and compile-time features is a major
> reason I like D. I argue rather that compile-time features should be
> *improved*. The current situation is good, but not quite there yet. It
> can be made better.
>
>
> T

Please see my other reply to Timor. "compile-time" is simply the run-time of the compiler and shouldn't require any special syntax. E.g. D currently requires me to use pragma to output a message during compilation whereas I think it would be much cleaner to simply use writeln() in a component loaded and run by the compiler during compilation of my target code.
April 28, 2012
On Sunday, April 29, 2012 01:42:05 bearophile wrote:
> Jonathan M Davis:
> >> - 'in' operator returning a pointer to the element.
> > 
> > Really? I use that all the time with AAs. Without that, you
> > would have to do
> > two lookups to get an object which might not be in the
> > container, so it would be less efficient.
> 
> LDC1 compiler introduced a small optimization, it looks for nearby associative array lookups and removes the second of them where possible. In my code I have seen this frees me to use "in" nearby followed by [], with the same efficiency of a single AA lookup. With this small optimization, D "in" is free to return a more clean boolean.

I really don't see anything "unclean" about returing a pointer. It does the job fantastically, and you can use it in conditional expressions just like you would use a bool. Also, while it may work for the most part, I'd be very skeptical about the compiler being able to optimize out lookups as well as simply using in would - especially in cases where you don't immediately use the pointer.

> This optimization doesn't work if you want to store the pointer returned by "in" to use it later, but in my code this pattern doesn't happen, I think it's quite uncommon.

Storing it for later use within the function is quite common. Storing it in a member variable or the like should be _very_ uncommon, since it's almost always a bad idea. But even just storing it in a local variable to use later could destroy the locality enough to defeat LDC's optimization.

- Jonathan M Davis
April 28, 2012
On Saturday, 28 April 2012 at 23:37:14 UTC, F i L wrote:
> Peter Alexander wrote:
>>> - opDispatch
>>>  This is useful and of significant value if used the right way.
>>
>> Can you give me an example of it being used the right way?
>
> It can be very useful for jQuery/JSON style dynamic objects:
>
>     import std.variant, std.stdio;
>
>     struct Dynamic {
>         Variant[string] vars;
>
>         void opDispatch(string key)() @property {
>             return vars[key];
>         }
>
>         void opDispatch(string key, T)(T value) @property {
>             vars[key] = value;
>         }
>     }
>
>     void main() {
>         auto foo = Dynamic();
>
>         foo.a = "A";
>         foo.b = 11;
>
>         writefln("%s, %s", foo.a, foo.b);
>     }

This is a perfect example of misuse.

Glancing at that code, it looks like foo has two member variables. It is also not clear that each access involves a hash-table lookup.

Why abuse well-understood syntax when there are simpler ways to solve the problem?


>>>  I hope you are not actually serious about that '->' part.
>>
>> I'm serious. I don't like overloaded syntax.  foo.bar shouldn't also mean (*foo).bar -- it causes confusion and introduces ambiguities when either could work. Combine this with opDispatch, UFCS and function overloading and your in for some nasty headaches.
>
> Craziness. What could you possibly gain is there in forcing a pointer distinction at every point that it's used? We already declared the variable as a pointer, we don't need to be reminded of that fact at every line.

It matters when you have things that imitate pointers (e.g. smart pointers). Those will have their own members, separate from the pointee's members, so ptr.foo could refer to the smart pointer's members or the pointee's members.

April 28, 2012
On Saturday, April 28, 2012 11:47:31 Walter Bright wrote:
> Andrei and I had a fun discussion last night about this question. The idea was which features in D are redundant and/or do not add significant value?
> 
> A couple already agreed upon ones are typedef and the cfloat, cdouble and creal types.
> 
> What's your list?

Well, that's a hard one. There isn't much in the language that I'd remove even if we were doing a complete redesign without caring about backwards compatibility. Most of the stuff that I'd change is already being (or has been) changed. And while D isn't exactly big, it doesn't tend to have dark corners like C++ does. I'm sure that I don't fully grasp everything in D, but it's fairly rare that I run into something that surprises me or that I thought that I understood but don't (though it still does happen sometimes). With C++, it always feels like there's some new detail that pops up that I didn't have a clue about - or that maybe I _did_ know about but had forgotten because of the insane number of little details that C++ has.

One _big_ difference between D and C++ as far as complexity goes is that in C++, a _lot_ of the complexity comes from weird things in the language and knowing about how certain things can go horribly wrong (e.g. I can never remember all of the details on how horribly broken multiple inheritence is). It's frequently not an issue of knowing how to do things in the language but rather an issue of knowing what weird side effects an problems happen with certain stuff. With D, on the other hand, the complexity tends to be in just knowing what all of the features are and what they can do.

The only feature that comes to mind as probably being overly complex is is expressions, but that complexity can really come in handy sometimes. std.traits should probably do more to alleviate the need for is expressions though so that odds of needing some of the more complicated stuff are lessened.

* As for redundant features, the first one that really comes to mind is WYSIWYG string literals. There are what, 4 different types of delimiters for string literals? I don't even remember all of the options. I just always use ` if I don't want any escaping in string literal and " if I do. The others may be valuable enough to have in some contexts, but I _never_ use them.

* version vs static if seems kind of redundant, but it isn't really. version is used for a very specific subset of conditional code compilation, and you can define versions on the command line, whereas static if generally checks code properties (like if a type meets certain conditions). So, I think that they should probably be left as is. There would be some value in allowing a list of versions to be given (rather than using logical operators) in order to avoid duplicate code - e.g.

version(linux, FreeBSD)
{}
else version(MacOSX)
{}
else version(Windows)
{}
else
    static assert(0);

but that's about the only thing that I'd change with version. And I wouldn't want to get rid of it.

* foreach_reverse is essentially redudant at this point (not to mention confusing if combined with delegates), since we have retro. But we might already be planning to get rid of that. I'm not sure though.

* I hate C style struct initializers and would really like to see them go, but for reasons that I don't understand, people actually use them rather than using a proper constructor call, so I doubt that we could get rid of them without a fair bit of complaining. I think that they're completely redundant and very _un_D.

* There seem to be too many ways to do variadic functions. We have to allow C style variadics for extern(C), but having 3 of our own seems like a bit much. Variadic templates are generally all that we need. The others don't seem necessary for the most part. But unfortunately, they should probably be left in so that classes can use them with virtual functions, since they can't use templates.


* As for features that add little value, the first one that comes to mind is with. I think that I've used it all of once, and I don't think that I've seen it in other people's code very often. It's hard to grep for (since with is used in comments quite often), but it wouldn't surprise me at all if Phobos doesn't use it at all. Also, I fear that not only does it add little value but that it has a tendency to make code less readable.

* Increasingly, I don't like UFCS. I think that in most cases, it complicates code for little value. And I _really_ don't like how it results in people flipping chains of a(b(c(d(5)))) calls into something like d(5).c().b.().a(). I think that it makes the code way harder to read. The code is completely backwards. But I appear to be in the minority with that opinion. I also don't like how it creates so many ways to write exactly the same code. It harms readibility. But as much as I dislike many of the applications of UFCS, it _does_ appear to be quite popular. And upon occasion, it may be useful, but I _am_ wishing that we hadn't added it.

There are definitely features that I never use (e.g. opDispatch), but for the most part, I think that they're stuff that adds real value for certain types of stuff that I just don't do, so removing them wouldn't really make any sense.

Overall, I think that D's feature set is fairly solid. It's mostly just the implementation of said features which is a problem - though some minor tweaks are probably in order (e.g. as Bearophile suggests, make it so that adjacent string literals don't concatenate).

- Jonathan M Davis
April 29, 2012
On Saturday, 28 April 2012 at 23:54:36 UTC, Peter Alexander wrote:
>
> It matters when you have things that imitate pointers (e.g. smart pointers). Those will have their own members, separate from the pointee's members, so ptr.foo could refer to the smart pointer's members or the pointee's members.

So basically, for your very specific issue, you want to ditch a syntax that 99% of people actually prefer. I must say you are the very first person I see *anywhere* who actually complains about the . notation and would rather go back to ->.
April 29, 2012
On Saturday, 28 April 2012 at 23:25:10 UTC, foobar wrote:
> On Saturday, 28 April 2012 at 20:43:38 UTC, Timon Gehr wrote:
>> On 04/28/2012 09:58 PM, foobar wrote:
>>> * version - this does not belong in a programming language. Git
>>> is a much better solution.
>>>
>>
>> So you'd maintain a git branch for every OS if there is some small part that is OS-dependent? I don't think that is a better approach at all.
>
> It is far better than having a pile of #ifdef styled spaghetti code.
> I'd expect to have all the OS specific code encapsulated separately anyway,
> not spread around the code base. Which is the current recommended way of using
> versions anyway. The inevitable conclusion would be to either use a version management system like git or have separate implementation modules for platform specific code and use the build tool to implement the logic of select the modules to include in the build.

 Aren't both of those just side effects of bad design? Most #ifdef calls were to overcome limits in the language that probably overloading would have resolved. I haven't done large projects code beyond my own, and the one or two I've seen were actually done in java.

 All Architecture and OS specific code should be hopefully done in just one file; That way ugly or special purpose code could be limited to one spot. Not only that but the rest of the code should be clean and you can build another architecture or system with minimal work. Course there will be cases where a lot of work would be needed, or in other cases no work at all.
April 29, 2012
On Sunday, April 29, 2012 01:54:35 Peter Alexander wrote:
> On Saturday, 28 April 2012 at 23:37:14 UTC, F i L wrote:
> > Peter Alexander wrote:
> >> I'm serious. I don't like overloaded syntax.  foo.bar shouldn't also mean (*foo).bar -- it causes confusion and introduces ambiguities when either could work. Combine this with opDispatch, UFCS and function overloading and your in for some nasty headaches.
> > 
> > Craziness. What could you possibly gain is there in forcing a pointer distinction at every point that it's used? We already declared the variable as a pointer, we don't need to be reminded of that fact at every line.
> 
> It matters when you have things that imitate pointers (e.g. smart pointers). Those will have their own members, separate from the pointee's members, so ptr.foo could refer to the smart pointer's members or the pointee's members.

For better or worse, the solution for smart pointers in D would be to use opDispatch, but it _does_ still mean that the type pointed to can't have any of the same functions as the smart pointer (or at least, they would have to be called differently, since . would use opDispatch and therefore use the smart pointer's function), so you _do_ lose something over the -> syntax here. However, the overall gain is usability probably still outweighs that. I wouldn't exactly have been heartbroken if -> had been in D, but simplicity of not needing it can be very nice - and it can be invaluable in the case of templates, because then you don't have to distinguish between structs, pointers to structs, and classes when calling functions on them.

- Jonathan M Davis
April 29, 2012
On Saturday, 28 April 2012 at 23:54:36 UTC, Peter Alexander wrote:
> On Saturday, 28 April 2012 at 23:37:14 UTC, F i L wrote:
>> Peter Alexander wrote:
>>>> - opDispatch
>>>> This is useful and of significant value if used the right way.
>>>
>>> Can you give me an example of it being used the right way?
>>
>> It can be very useful for jQuery/JSON style dynamic objects:
>>
>>    import std.variant, std.stdio;
>>
>>    struct Dynamic {
>>        Variant[string] vars;
>>
>>        void opDispatch(string key)() @property {
>>            return vars[key];
>>        }
>>
>>        void opDispatch(string key, T)(T value) @property {
>>            vars[key] = value;
>>        }
>>    }
>>
>>    void main() {
>>        auto foo = Dynamic();
>>
>>        foo.a = "A";
>>        foo.b = 11;
>>
>>        writefln("%s, %s", foo.a, foo.b);
>>    }
>
> This is a perfect example of misuse.
>
> Glancing at that code, it looks like foo has two member variables. It is also not clear that each access involves a hash-table lookup.
>
> Why abuse well-understood syntax when there are simpler ways to solve the problem?

It's not abuse, it's design. That's why I called the type "Dynamic" so the user has an idea about what it is. You could make your same arguments against ever using a Variant object. When you have to manipulate a DOM or XML tree, there's reason why people prefer languages that syntactically interface with those dynamic structure very well. D simply powerful enough to provide a way for programmers to do both.

Btw, it's just as unclear that any @property (or function) doesn't look through a hash-table or any other number of performance pitfalls if used without any insight into _how_ to use it. This is why we have documentation for libraries, it's impractical cater the language to the idea that coders wont understand the objects their working with to a basic degree.


>>>> I hope you are not actually serious about that '->' part.
>>>
>>> I'm serious. I don't like overloaded syntax.  foo.bar shouldn't also mean (*foo).bar -- it causes confusion and introduces ambiguities when either could work. Combine this with opDispatch, UFCS and function overloading and your in for some nasty headaches.
>>
>> Craziness. What could you possibly gain is there in forcing a pointer distinction at every point that it's used? We already declared the variable as a pointer, we don't need to be reminded of that fact at every line.
>
> It matters when you have things that imitate pointers (e.g. smart pointers). Those will have their own members, separate from the pointee's members, so ptr.foo could refer to the smart pointer's members or the pointee's members.

SomeDude said this best. This is far the common case, and the language shouldn't force everyone to pay for this situation.


April 29, 2012
Typos. Typos everywhere :S I'm sure you get my meaning.
April 29, 2012
On Saturday, 28 April 2012 at 23:50:22 UTC, foobar wrote:
> On Saturday, 28 April 2012 at 21:02:25 UTC, H. S. Teoh wrote:
>>> * di files - a library should encapsulate all the info required
>>> to use it. Java Jars, .Net assemblies and even old school; Pascal
>>> units all solved this long ago.

> I agree with the general notion here. Whatever the actual implementation details are, the API should be strongly tied to the binary in order to insure consistency and ease of use. I shouldn't need to worry if the header files match the binary library. Regarding the human readable API - that's why we have documentation for.

 Mmm well the main reason I see using .di files, is cases when the input library/file/dll doesn't give you much or any information. like... most dll's today. There's also tools to strip that extra debugging and structure information from your output file, so if you distribute a binary only, you still need to include it's .h file or .di file.

 Cases where this would be far more relevant could be in systems that don't have a lot of room (mini-distros or recovery disks for example). I've seen a recovery disk distro with everything you needed 2 floppies disks. Only reason I don't use floppies anymore is the ones being made are crap and don't keep data where as 14 years ago I could accidentally put mine through the wash and still access it's contents. (Cheap bastards)