April 28, 2012
On Saturday, April 28, 2012 23:20:42 Adam D. Ruppe wrote:
> On Saturday, 28 April 2012 at 18:48:18 UTC, Walter Bright wrote:
> > What's your list?
> 
> I think most the responses to this thread are insane.

LOL. Yeah. Many of them involve completely gutted pieces of the language, and very few discuss "redundant" features like Walter mentioned.

- Jonathan M Davis
April 28, 2012
On Saturday, 28 April 2012 at 22:33:08 UTC, Timon Gehr wrote:
> - UFCS:
>    The complexity comes from having multiple function invocation
>    syntaxes. UFCS actually makes that situation better without adding a
>    lot of complexity to the compiler implementation.

Exactly. The problem is having multiple function invocation syntaxes. That's one source of complexity, and UFCS add another in attempt to reduce the first cause.


> - const/immutable/shared/pure
>    shared: The fact that everything that is not marked as shared is
>    actually thread-local is extremely important. I think most other
>    imperative languages got this wrong.
>    But if shared is explicit in the type system, immutable really
>    should be explicit too. The sad part is that the qualifiers don't
>    play nicely with reference types at the moment.

I agree with thread-local by default, but that is separate from shared.


> - 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?


>    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.
April 28, 2012
On Saturday, 28 April 2012 at 23:11:17 UTC, Peter Alexander wrote:
> On Saturday, 28 April 2012 at 22:33:08 UTC, Timon Gehr wrote:
>> - UFCS:
>>   The complexity comes from having multiple function invocation
>>   syntaxes. UFCS actually makes that situation better without adding a
>>   lot of complexity to the compiler implementation.
>
> Exactly. The problem is having multiple function invocation syntaxes. That's one source of complexity, and UFCS add another in attempt to reduce the first cause.
>
>
>> - const/immutable/shared/pure
>>   shared: The fact that everything that is not marked as shared is
>>   actually thread-local is extremely important. I think most other
>>   imperative languages got this wrong.
>>   But if shared is explicit in the type system, immutable really
>>   should be explicit too. The sad part is that the qualifiers don't
>>   play nicely with reference types at the moment.
>
> I agree with thread-local by default, but that is separate from shared.
>
>
>> - 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?
>

vector swizzling! :D

>
>>   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.


April 28, 2012
On Saturday, 28 April 2012 at 20:43:38 UTC, Timon Gehr wrote:
> On 04/28/2012 09:58 PM, foobar wrote:
>> On Saturday, 28 April 2012 at 18:48:18 UTC, 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?
>>
>> D has a lot of ad-hock features which make the language
>> needlessly large and complex. I'd strive to replace these with
>> better general purpose mechanisms.
>>
>> My list:
>> * I'd start with getting rid of foreach completely. (not just
>> foreach_reverse).
>
>
> foreach is very useful. Have you actually used D?
>

I have used D and didn't claim that foreach isn't useful.
What I said that is that it belongs in the library, NOT the language.

>> This is nothing more than a fancy function with
>> a delegate parameter.
>>
>
> That would be opApply.

Indeed but I'd go even further by integrating it with ranges so that ranges would provide an opApply like method e.g.
auto r = BinaryTree!T.preOrder();  // returns range
r.each( (T elem) { ...use elem...}); // each method a-la Ruby

>
>> * enum - enum should be completely redesigned to only implement
>> what it's named after: enumerations.
>>
>
> What is the benefit?

On the one hand the current enum for manifest constants is a hack due to weaknesses of the toolchain and on the other hand it doesn't provide properly encapsulated enums such as for instance the Java 5.0 ones or the functional kind.

>
>> * 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.

>
>> * 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.
>>
>> * This is a big one: get rid of *all* current compile time
>> special syntax.
>
> What would that be exactly?

This includes __traits, templates, static ifs, etc..

>
>> It should be replaced by a standard compilation
>> API and the compiler should be able to use plugins/addons.
>
> Are you serious?

No I'm joking.

The current system is a pile of hacks on top of the broken model of c++ templates.

I should be able to use a *very* minimalistic system to write completely _regular_ D code and run it at different times. This is a simple matter of separation of concerns: what we want to execute (what code) is separate to the concern of when we want to execute it.


>
>> This would reduce the size of the language to half of its current
>> size, maybe even more.
>
> I am certain that it would not.
>
>
> You missed to present the 'general purpose mechanisms'.
April 28, 2012
Peter Alexander:

> f(x) ---> x.f() is not progress in language design.

I used to think the same. But Haskell offers "." and $ to chain functions and remove parentheses, F# has the  |>  pipe operator. In D UCFS is almost equally useful to turn nesting of function calls in a more readable chain. In functional-style code this makes a lot of difference, turning:

foo(bar(copy(baz(a), spam(b))))

Into:

baz(a).copy(b.spam()).bar().foo()

When I see 3+ nested parentheses I find it hard to read the expression. While a chain is easy to read.


> In theory. IMO the amount of time you spend trying to please the type system could have been better spent optimizing your code manually.
>
> No code I have ever written would benefit significantly from these potential optimisations. For example, one optimisation is that pure function calls could be hoisted out of loops. Great, except that if that function call had any significant cost whatsoever, I would have already done that optimisation myself.

I used to write lot of D1 code too, and I've seen that managing D2 const-related issues slows down significantly my coding compared to D1 coding, so undeniably they have a cost.

But in D the main purpose of "pure" is not as optimization tool, but more as a tool to enforce a better coding style, that makes code understanding (and testing simpler), and helps avoid some bugs, coming from using variables from outer scopes.

Bye,
bearophile
April 28, 2012
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);
    }


>>   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.
April 28, 2012
On Saturday, 28 April 2012 at 21:19:00 UTC, H. S. Teoh wrote:
> On Sat, Apr 28, 2012 at 10:07:54PM +0200, q66 wrote:
>> On Saturday, 28 April 2012 at 20:04:11 UTC, Walter Bright
> The whole point of a standard library is that if somebody has  written
> that code before, and it's general enough for everyday use, then you
> shouldn't need to download this, install that, configure the other,
> before you can use it. It's a major plus if you're publishing code to be
> able to say, just download my sources and compile it with the language
> standard library and it will all work. As opposed to, if you want to
> compile my code, you need library X and Y which depend on W and Z, all
> of which have to be downloaded from different places all over the 'net
> and you better make sure you get the right versions otherwise everything
> will break.

 OMG I love you :)

 A long time ago when I began programming I came to a distinct conclusion that anything I write has to be able to compile and just work with only the standard library. I don't want to make people go out of their way to get this or that.

* If you have something that requires multimedia, then fine a library will help.
* If you have to have something that does translations between other languages and makes sense, then use it.

 But for everything that's just basic work processing and otherwise, there is no reason you should have an external library used. Need compression? If the standard library (like C) doesn't include something, then what do you choose? LZMA? lzop? gzip/zlib? bzip2? Depends on how heavy duty you need. If a basic compression is included then you will use it unless you REALLY need the extra speed or compression ratio.

 "Make everything as simple as possible, but no simpler." -- Albert Einstein
April 28, 2012
On Saturday, 28 April 2012 at 23:29:35 UTC, bearophile wrote:
> Peter Alexander:
>
>> f(x) ---> x.f() is not progress in language design.
>
> I used to think the same. But Haskell offers "." and $ to chain functions and remove parentheses, F# has the  |>  pipe operator. In D UCFS is almost equally useful to turn nesting of function calls in a more readable chain. In functional-style code this makes a lot of difference, turning:
>
> foo(bar(copy(baz(a), spam(b))))
>
> Into:
>
> baz(a).copy(b.spam()).bar().foo()
>
> When I see 3+ nested parentheses I find it hard to read the expression. While a chain is easy to read.

What D does and what Haskell does are very different things. D has (at least) two types of functions: free functions and member functions. UFCS makes free functions look like member functions. In Haskell, $ just gives you a way of re-ordering precedence -- it doesn't hide anything.

This matters because UFCS in D is deceitful. It makes you think the free function is a member function when it is not.

struct Foo { void bar() {} }
void baz(Foo f) {}

Foo f;
f.bar(); // ok
f.baz(); // ok, looks like baz is a member function
auto pbar = &Foo.bar; // ok
auto pbaz = &Foo.baz; // error!

IMO, D would be better with Haskell's function calling syntax (of course, this would require many, many other syntactical changes).


> But in D the main purpose of "pure" is not as optimization tool, but more as a tool to enforce a better coding style, that makes code understanding (and testing simpler), and helps avoid some bugs, coming from using variables from outer scopes.

True, but I'm quite happy to write pure functions without the static checking. I do not believe that the safety provided by the static checks outweighs the development cost of ensuring you have the correct qualifiers everywhere.
April 28, 2012
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.

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.

Java/JavaScript/Lua show that the language design has to focus on what's hard to optimize away for the compiler, like offering ways to the programmer to specify higher level semantics (like asserting: foo(a,b) is equal to foo(b,a)), instead of focusing on what a not dumb static compiler (not even a JIT) is able to optimize away.

Bye,
bearophile
April 28, 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:
>>> On Saturday, 28 April 2012 at 18:48:18 UTC, 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?
>>>
>>> D has a lot of ad-hock features which make the language
>>> needlessly large and complex. I'd strive to replace these with
>>> better general purpose mechanisms.
>>>
>>> My list:
>>> * I'd start with getting rid of foreach completely. (not just
>>> foreach_reverse).
>>
>>
>> foreach is very useful. Have you actually used D?
>>
>
> I have used D and didn't claim that foreach isn't useful.
> What I said that is that it belongs in the library, NOT the language.
>

Well, it's your opinion. But I bet it's not the opinion of thousands of programmers, it's not the opinion of the Java/C# designers, and I even believe they wanted to add foreach in C++ (or is it already the case ?).
Putting things in the library isn't the solution for everything: it's often hard (if possible) to make it work as well as in the core language, and error messages are usually more cryptic. Basic features like this should stay in the core language in my opinion.

>>> This is nothing more than a fancy function with
>>> a delegate parameter.
>>>
>>
>> That would be opApply.
>
> Indeed but I'd go even further by integrating it with ranges so that ranges would provide an opApply like method e.g.
> auto r = BinaryTree!T.preOrder();  // returns range
> r.each( (T elem) { ...use elem...}); // each method a-la Ruby
>
>>
>>> * enum - enum should be completely redesigned to only implement
>>> what it's named after: enumerations.
>>>
>>
>> What is the benefit?
>
> On the one hand the current enum for manifest constants is a hack due to weaknesses of the toolchain and on the other hand it doesn't provide properly encapsulated enums such as for instance the Java 5.0 ones or the functional kind.
>
>>
>>> * 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.
>

No it isn't. Ask the kernel hackers why they still use #ifdef instead of using hundreds of git branches for every feature and platform they must maintain.

>>
>>> * 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.
>>>
>>> * This is a big one: get rid of *all* current compile time
>>> special syntax.
>>
>> What would that be exactly?
>
> This includes __traits, templates, static ifs, etc..
>
>>
>>> It should be replaced by a standard compilation
>>> API and the compiler should be able to use plugins/addons.
>>
>> Are you serious?
>
> No I'm joking.
>
> The current system is a pile of hacks on top of the broken model of c++ templates.
>
> I should be able to use a *very* minimalistic system to write completely _regular_ D code and run it at different times. This is a simple matter of separation of concerns: what we want to execute (what code) is separate to the concern of when we want to execute it.
>
>
>>
>>> This would reduce the size of the language to half of its current
>>> size, maybe even more.
>>
>> I am certain that it would not.
>>
>>
>> You missed to present the 'general purpose mechanisms'.


You should use Go. It fits better to your views about programming languages than D.