December 28, 2010
Andrei:

> FWIW I just posted a response to a question asking for a comparison between Clay and D2.
> 
> http://www.reddit.com/r/programming/comments/es2jx/clay_programming_language_wiki/

Just few comments:

> The docs offer very little on Clay's module system (which is rock solid in D2).

D2 module system may be fixed, but currently it's not even bread-solid.

The Clay syntax for imports is more similar to what I have desired for D (but that () syntax is not so good):

  import foo.bar; // Imports module foo.bar as a qualified path
  // use "foo.bar.bas" to access foo.bar member bas
  import foo.bar as bar; // Imports module foo.bar with alias bar
  // use "bar.bas" to access foo.bar member bas
  import foo.bar.(bas); // Imports member bas from module foo.bar
  // use "bas" to access foo.bar member bas
  import foo.bar.(bas as fooBarBas) // Imports member bas with alias fooBarBas
  import foo.bar.*; // Imports all members from module foo.bar

I don't know about Modula3 module system, I will search info about it.


>Clay mentions multiple dispatch as a major feature. Based on extensive experience in the topic I believe that that's a waste of time. Modern C++ Design has an extensive chapter on multiple dispatch, and I can vouch next to nobody uses it in the real world. Sure, it's nice to have, but its actual applicability is limited to shape collision testing and a few toy examples.<

I think double dispatch is enough, it cover most cases and keeps both compiler complexity low enough. If you put double dispatch with a nice syntax in D then maybe people will use it. There are many things that people in other languages use that C++ programmers don't use because using it in C++ is ugly, a pain, unsafe, etc. The visitor pattern is used enough in Java (Scala too was designed to solve this problem).

Bye,
bearophile
December 28, 2010
bearophile wrote:

> The Clay syntax for imports is more similar to what I have desired for D:

That looks virtually identical to D, with the sole exception of "import module" there means "static import module" in D and "import module.*" is "import module".

But the functionality is identical. I've seen you bring up this import thing over and over again, and every time someone points out D already does what you ask for and has since forever ago, but you always bring it up again.

I don't understand what your problem is with this. Is your only complaint that import all is the default? If yes, why did you show 3 irrelevant things there instead of just the two that directly reflect what you are trying to say?


By the way, I find D's system to be perfect. It Just Works in most situations, and fails to compile meaningfully in the rest, trivially fixed by putting the absolute module path where it is needed and nowhere else. This is rock solid today. (I assume when you said bread solid you were referring to package protection and stuff like that. But that's separate from the import statement, so again, why not say it directly instead of mixing it with so much irrelevant stuff?)

That's the ideal situation. Writing out qualified paths by default is just awful, and I can't understand why you keep asking for it.
December 28, 2010
On 12/28/10 12:09 AM, bearophile wrote:
> Andrei:
>
>> FWIW I just posted a response to a question asking for a comparison
>> between Clay and D2.
>>
>> http://www.reddit.com/r/programming/comments/es2jx/clay_programming_language_wiki/
>
> Just few comments:
>
>> The docs offer very little on Clay's module system (which is rock solid in D2).
>
> D2 module system may be fixed, but currently it's not even bread-solid.
>
> The Clay syntax for imports is more similar to what I have desired for D (but that () syntax is not so good):
>
>    import foo.bar; // Imports module foo.bar as a qualified path
>    // use "foo.bar.bas" to access foo.bar member bas
>    import foo.bar as bar; // Imports module foo.bar with alias bar
>    // use "bar.bas" to access foo.bar member bas
>    import foo.bar.(bas); // Imports member bas from module foo.bar
>    // use "bas" to access foo.bar member bas
>    import foo.bar.(bas as fooBarBas) // Imports member bas with alias fooBarBas
>    import foo.bar.*; // Imports all members from module foo.bar
>
> I don't know about Modula3 module system, I will search info about it.
>
>
>> Clay mentions multiple dispatch as a major feature. Based on extensive experience in the topic I believe that that's a waste of time. Modern C++ Design has an extensive chapter on multiple dispatch, and I can vouch next to nobody uses it in the real world. Sure, it's nice to have, but its actual applicability is limited to shape collision testing and a few toy examples.<
>
> I think double dispatch is enough, it cover most cases and keeps both
> compiler complexity low enough. If you put double dispatch with a
> nice syntax in D then maybe people will use it. There are many things
> that people in other languages use that C++ programmers don't use
> because using it in C++ is ugly, a pain, unsafe, etc. The visitor
> pattern is used enough in Java (Scala too was designed to solve this
> problem).

This is... out there. I can't stop wondering - on what basis do you make such infinitely confident assertions? Have you worked on any large scale C++ system, or on a C++ system of any scale for that matter? Have you built one or more systems in whatever language in which multiple dispatch was an enabling feature?

A competent C++ programmer will use a pattern such as Visitor or double dispatch if needed. Double dispatch is not ugly in C++ (as Modern C++ Design has shown ten years ago), and even if it were, people who need it would still use it. Also, Visitor looks and acts at least as good in C++ (compared to e.g. Java) with the help of templates and macros.

Speaking from direct experience: although double dispatch as implemented in Modern C++ Design was (and probably still is) best of the breed, based on the feedback I got (better said, I didn't get), I can confidently say people seldom need it. There were major bugs in the implementation that lasted for seven years before anyone noticed them. In contrast, bugs in virtually all other chapters of the book were very quick to show up. This is because people needed the other patterns, but didn't need double dispatch.

Please, bearophile, if you are looking for a New Year's resolution, vow to stop feigning more competence than you have. You are plenty good, we all appreciate you, but please stick with what you actually know, which is a lot. I'm sorry but it would be a full-time job for me or anyone to debunk the occasional enormity you write, so I must resort to being frank just this once. Hope you understand. Thank you.


Andrei
December 28, 2010
bearophile wrote:
>> The docs offer very little on Clay's module system (which is rock solid in
>> D2).
> D2 module system may be fixed, but currently it's not even bread-solid.

It does all of your wish list, and more. I've pointed this out to you before even in the last month. Please read the documentation.

http://www.digitalmars.com/d/2.0/module.html

D's anti-hijacking support in imports is unique to D, and second to none.

http://www.digitalmars.com/d/2.0/hijack.html
December 28, 2010
Adam D. Ruppe:

> That's the ideal situation. Writing out qualified paths by default is just awful, and I can't understand why you keep asking for it.

Importing all names from a module on default is against modularity. While you read a module code you don't know where the imported names it uses come from. Take also a look at how Ada specifies interfaces across modules. A language needs to be designed with a balance between low verbosity and safety, here in my opinion D has chosen too much for the low verbosity (as Walter reminds D's anti-hijacking support in imports is a nice idea, but I think it's not enough).

(Reading about safer language subsets like SPARK and MISRA I have learnt that having a language safe on default is better (because this safety spreads in the whole ecosystem of the language users, and not just in a small subset of it), but where that's not possible, then sometimes an acceptable replacement is to have a language design that allows an external tool to statically disallow an unsafe coding style or feature (in many cases this is not possible for C, or it's possible but leads to a very limited language). In this case for an external tool it's easy to statically disallow not-static D imports.)

Bye,
bearophile
December 28, 2010
Adam D. Ruppe:

> Writing out qualified paths by default
> is just awful, and I can't understand why you keep asking for it.

If in your module you don't want to use qualified paths you use: import foo: bar, spam;

To import all not-private names of the foo module use: import foo: *;

Recently Andrei has said that syntax is blunt, preferring the user defined "all" name solution (an "all" module in a package). So to define a less blunt import semantics you may add a * to the module you use as name pack:
import foo: all*; // imports all names in the "all" name pack

Bye,
bearophile
December 28, 2010
bearophile:

> import foo: all*; // imports all names in the "all" name pack

Or just:
import foo.all: *;

Bye,
bearophile
December 28, 2010
On 12/28/2010 11:19 AM, bearophile wrote:
> Adam D. Ruppe:
>
>> That's the ideal situation. Writing out qualified paths by default
>> is just awful, and I can't understand why you keep asking for it.
>
> Importing all names from a module on default is against modularity. While you read a module code you don't know where the imported names it uses come from. Take also a look at how Ada specifies interfaces across modules. A language needs to be designed with a balance between low verbosity and safety, here in my opinion D has chosen too much for the low verbosity (as Walter reminds D's anti-hijacking support in imports is a nice idea, but I think it's not enough).
>
> (Reading about safer language subsets like SPARK and MISRA I have learnt that having a language safe on default is better (because this safety spreads in the whole ecosystem of the language users, and not just in a small subset of it), but where that's not possible, then sometimes an acceptable replacement is to have a language design that allows an external tool to statically disallow an unsafe coding style or feature (in many cases this is not possible for C, or it's possible but leads to a very limited language). In this case for an external tool it's easy to statically disallow not-static D imports.)
>
> Bye,
> bearophile

Taking an example from std.algorithm documentation:

1)
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
auto squares = map!("a * a")(chain(arr1, arr2));
assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
// 146 characters

2)
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
auto squares = std.algorithm.map!("a * a")(std.range.chain(arr1, arr2));
assert(std.algorithm.equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
// 184 characters

How is 2) is better/safer than 1)? I took a pretty short example, but I easily imagine as real code would blow up to 25-40% more characters just for the sake of explicit qualification, especially when using third-party (or own) libraries with nested structure (like, e.g., in Tango). And nothing is actually gained with this, except that it is "clear" from what modules those names come. In C++, you sometimes have to fully qualify names "just in case" to prevent hijacking. But D is hijacking-proof at compile time, so there is no need in those "just in case" qualifiers, the compiler will tell you when the "case" is here.

And what would you propose to do with uniform function call syntax if "import all" is not the default? Even considering that currently it works only for arrays, would you replace every use arr.front, arr.back, arr.empty with std.array.front(arr), std.array.back(arr) and std.array.empty(arr) in favor of "import safety"? Or would you instead put a long "disqualifying" import line at the top of every module that uses std.array?

My resume:

> Importing all names from a module on default is against modularity.

It is not. It does not break modularity, it simplifies module interfacing, it reduces amount of code (and is therefore DRY-friendly).

> If in your module you don't want to use qualified paths you use:
import foo: bar, spam;

...which quickly expands to a lot of *long* import lines, with "don't forget to add another" constantly pushing the door-bell.
December 28, 2010
Stanislav Blinov:

> ...which quickly expands to a lot of *long* import lines, with "don't forget to add another" constantly pushing the door-bell.

That's redundancy is "making your subsystems interfaces explicit". I see that you and other people here fail to understand this, or just value a bit of typing more than a tidy way of coding. To interested people I suggest to read papers about this part of the Ada design (and generally about module system design).

Bye,
bearophile
December 28, 2010
bearophile wrote:
> (Reading about safer language subsets like SPARK and MISRA I have learnt that
> having a language safe on default is better

Reading about a language is not good enough to make such decisions. You need to have considerable experience with them. I've seen a lot of claims about languages that simply do not pan out. Exception specifications in Java is a prime example - it took years of experience to discover that was a really bad idea. It had the opposite effect from what was intended (and touted).


> Take also a look at how Ada specifies interfaces across modules.

Ada is a failed language.


> A language needs to be designed with a balance between low verbosity and safety, here in my opinion D has chosen too much for the low verbosity (as Walter reminds D's anti-hijacking support in imports is a nice idea, but I think it's not enough).

D's anti-hijacking nails it. You get the benefits of low verbosity, and it's perfectly safe. It's a lot more than just a "nice idea".