December 28, 2010
bearophile wrote:
> bearophile:
> 
>> import foo: all*; // imports all names in the "all" name pack
> 
> Or just:
> import foo.all: *;

Better yet:

   import foo.all;

and it's even implemented!
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.


Walter:
> 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).

You are both right. I have to use those languages (like Ada) for something more than tiny programs, and then maybe I will be able to bring here a bit of experimental evidence :-) Sorry for the noise and for using some of your time.

Bye,
bearophile
December 28, 2010
On 12/28/2010 03:26 PM, bearophile wrote:
> 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).
>

What I do fail to understand is what should these explicit interfaces bring *into D*. Until now, you didn't demonstrate anything they would give that D doesn't already have.

BTW, I forgot to mention one more point: explicit qualification has significant impact on generic code:

void foo(T)(T t) if (__traits(compiles, bar(t))
{
	bar(t);
}

How would that be scalable to new types if bar would have to be explicitly qualified?

void foo(T)(T t) if (__traits(compiles, pkg.modbar.bar(t))
{
	pkg.modbar.bar(t);
}

Now, to extend the range of types accepted by foo(), you *have to* change pkg.modbar module to introduce bar() for new types, which isn't always possible or desirable.
December 28, 2010
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

This looks like a subset of the D module system. The only difference is
'static' import by default and the .* feature which has to be implemented by
the library author in D. Are those the things you find missing in D?

> 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

I think I agree here. I have never programmed much in a language with multiple dispatch, but everytime I see dynamic casting or the visitor pattern in an OOP program I think about how that would be so much better. Honestly its just speculation, but I guess that the lack of interest is because it is not a widely known and available solution. Library implementations have issues, both usability and performance wise.
December 28, 2010
bearophile wrote:
> 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

Bearophile, you frequently talk about "a tidy way of coding" in arguments. As far as I can tell, it simply means "bearophile's way of coding", and it's always verbose, but the 'tidy' contains an implied value judgement that it is good.

Your use of 'tidy coding' doesn't distinguish between a coding style which catches bugs, versus a coding style which contains needless, redundant verbosity -- and that is inevitably what the discussion is about. I do not think it is ever valid to use that expression.

To me, that term is always a flag that a fallacy is coming.

December 28, 2010
> While you read a module code you don't know where the imported names it uses come from.

That's a feature. Let me give an example. I recently wrote an OAuth implementation for my web app.

I started off by writing it all in one module, the one where it
was used. That was ok until I wanted to use it in a different module,
so I cut and pasted it to a different file, the shared util.d.

Compile, both modules now work. No need to edit the user code at all.

Next up I moved it from that grab bag of project specific utilities to a generic oauth.d module, refactoring it to be generic.

Add "import oauth;" and compile. All modules work without editing their code. Later, I might choose to rename it to something more generic. If I added another web mechanism, I might call it webauth.d or something like that. Aside from the import lines, the rest of the user code would confidently remain unchanged.


That's the advantage a good import system like D has. You can
make changes with ease and confidence knowing it isn't going to
break anything anywhere else. That's really the goal of modularity:
making it so a change in one place won't break code anywhere else.

Using fully qualified names works /against/ that.

(Note though that sometimes fully qualified names do make things more clear or show something special about that block of code. They have their uses and it makes me very happy D supports it and has static imports. But their uses are more specialized: being the default would be a net negative, for the reasons above.)
December 28, 2010
Let me tell a horror story about what is definitely bad modularity: something that happened in the old PHP app.

(I might pick on PHP a lot, but that's just because it is the
language I have the most professional experience in so I've seen
a lot of its horrors first hand. That, and it is a godawful language!)

So I had to add a function to one of the project's includes to enable new functionality. I didn't even get very far into it before a mysterious error came up across the site:

PHP Fatal error: Cannot redeclare bar_foo() (previously declared
in my file) in completely unrelated file on line x.


WTF. I coincidentally picked the same name for my function
as my predecessor did for another function a long time ago, in
a file far, far away! And it broke things scattered throughout
the program. C does this too, but at least there's a compile
step to save you.

The sad part: that's the best case scenario with PHP! If it hadn't
broken the entire site, I might not have caught it so early and
then there'd be a real mess on the one page that just happened
to include both files at once, hidden, lurking...

But hell, at least if you coincidentally pick the same name as your predecessor it doesn't *overwrites* one of your functions at runtime like some languages! I kid you not, some popular languages actually do that. It blows my mind.



Anyway, that's bad modularity and a couple of bug prone behaviors
coming from it. Using a strict naming convention with long, fully
qualified names (like familyname_functionname convention) might
be a good thing in those languages, to reduce the odds that two
team members pick the same name and break code elsewhere in the app.

In D, however, such things are not necessary. The compiler makes it Just Work or fail early and fail meaningfully in the exact place where there actually is a problem and nowhere else. Such problems cannot occur, so the logic that goes into protecting yourself in other languages don't necessarily apply to D.
December 28, 2010
On 12/28/2010 11:49 AM, Adam D. Ruppe wrote:
> Let me tell a horror story about what is definitely bad modularity:
> something that happened in the old PHP app.
>
> (I might pick on PHP a lot, but that's just because it is the
> language I have the most professional experience in so I've seen
> a lot of its horrors first hand. That, and it is a godawful language!)
>
> So I had to add a function to one of the project's includes to enable
> new functionality. I didn't even get very far into it before a
> mysterious error came up across the site:
>
> PHP Fatal error: Cannot redeclare bar_foo() (previously declared
> in my file) in completely unrelated file on line x.
>
>
> WTF. I coincidentally picked the same name for my function
> as my predecessor did for another function a long time ago, in
> a file far, far away! And it broke things scattered throughout
> the program. C does this too, but at least there's a compile
> step to save you.
>
> The sad part: that's the best case scenario with PHP! If it hadn't
> broken the entire site, I might not have caught it so early and
> then there'd be a real mess on the one page that just happened
> to include both files at once, hidden, lurking...
>
> But hell, at least if you coincidentally pick the same name as
> your predecessor it doesn't *overwrites* one of your functions at
> runtime like some languages! I kid you not, some popular languages
> actually do that. It blows my mind.
>
>
>
> Anyway, that's bad modularity and a couple of bug prone behaviors
> coming from it. Using a strict naming convention with long, fully
> qualified names (like familyname_functionname convention) might
> be a good thing in those languages, to reduce the odds that two
> team members pick the same name and break code elsewhere in the app.
>
> In D, however, such things are not necessary. The compiler makes
> it Just Work or fail early and fail meaningfully in the exact
> place where there actually is a problem and nowhere else. Such
> problems cannot occur, so the logic that goes into protecting
> yourself in other languages don't necessarily apply to D.

This actually reminds me of an article I read quite a while ago that praised Erlang for how it handles functions in different modules.  It requires that you prefix the name of any function that is imported with the name of the module you are importing from.  So, if you want to use the function bar from module foo, you write something like this:

foo:bar(1),

Now, the nice thing about this is that you immediately see what module this function came from.  Also, it helps ensure that your function names are truly unique since they now include the module name.  However, the bad part is that you have more to type.  Personally, I find that to be a minor issue compared to the benefits, but that's just me.

Am I saying this is how it should work in D?  No, but I think it can be done now anyway if IIRC.  I'll have to look at the docs.  Regardless, it may be a good "best practice" for those times when you are working on a large application.

Casey
December 28, 2010
sybrandy wrote:
> Now, the nice thing about this is that you immediately see what module this function came from.

Yes, that's like a static import in D. You can also use fully qualified names with a standard import:

std.stdio.File -- optional long form for File

static import std.stdio; // you *must* use the long form for all names


> However, the bad part is that you have more to type.

That's not the only bad part. It also means refactoring your modules requires changes to the user code too. See my other post here:

http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=125420
December 28, 2010
On 12/28/10, Adam Ruppe <destructionator@gmail.com> wrote:
> That's not the only bad part. It also means refactoring your modules requires changes to the user code too. See my other post here:

Actually, D is equipped to solve even that problem. If you really want to use fully qualified names and reserve the right to rename a module, you can do this:

foo.d:
import std.stdio : writeln;
void bar()
{
    writeln("bar");
}

main.d:
static import foo = foo;
void main()
{
    foo.bar();
}

If you decide to rename the foo module to "foobar", all you need to
change is one line in main:
static import foo = foobar;

This is much saner than bearophile's "force full qualified names by default", since it still allows you to refactor your modules with the minimum amount of changes in the importing code.