December 28, 2010
Andrej Mitrovic:

> This is much saner than bearophile's "force full qualified names by default",

I have never said that to force that. I have said to not import all names from a module on default. But there are three ways to avoid that when you want it: list the names you want, use a star, or use a star+pack name (like all).

Bye,
bearophile
December 28, 2010
Andrej Mitrovic wrote:
> Actually, D is equipped to solve even that problem

Indeed.

Though, that doesn't cover all the uses of shuffling things around. My oauth.d module currently still includes some of my own specific app, like my own api secret. (It was originally hard coded, then I moved it to a helper function, but the helper function is still in the generic module instead of moved out.)

It looks like this:

oauthRequest(struct OAuthParams, request details...);

immutable myAppParams = OAuthParams("api key", "api secret", other);


In user code:

oauthRequest(myAppParams, ...);


Soon, I'll cut and paste that myAppParams out and move it to the app-specific config.d, making oauth.d itself fully generic.


If I was using all fully qualified names, even if renamed:


oauth.oauthRequest(oauth.myAppParams, ...);

And I'd then have to change all that usage to:

oauth.oauthRequest(config.myAppParams, ...);



Relatively minor in this situation - at least it would tell me at compile time that oauth.myAppParams no longer exists, so it is an easy fix, but you can see how this could become very annoying as the usage grows.
December 29, 2010
On 12/28/2010 12:37 PM, Andrej Mitrovic wrote:
> 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;

I do like this and I did think about this for a different reason: avoiding long/obnoxious module names.  (E.g. Java-like)  However, this is another good reason to use this feature.

Casey
December 29, 2010
On Tue, 28 Dec 2010 14:38:56 +0300
Stanislav Blinov <stanislav.blinov@gmail.com> wrote:

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

It seems you don't get the point (or rather what I think is the actual point). "Safe" import (as the OP defines and names it; I would rather call it "explicite import") perfectly allows aliasing imported symbols, as D presently does it:
	import mod	: func = somefunc; // or simply func=func
But the current default / least resistance way of importing blindly imports all symbols from the module in a local namespace and --even more importantly for me:
1. at import place, does not tell the reader which symbols are actually used
2. at use place, does not tell the reader where a given symbol is imported from
These are imo essential documentation needs unfulfilled. (I'm constantly bumping in this wall when reading other people's code; esp. phobos source; even more when it uses C stdlib funcs that everybody but me seems to know where they are defined, what they actually do, and how ;-)

Keeping the module name instead of aliasing in numerous cases makes the code far easier to read. For instance, I commonly use this scheme:
	static import file = std.file;
	...
	auto config = file.readText("config");

There should be a way to carelessly import everything --for instance into a parser module using (most) pattern types, constants, and more, from a parsing lib. This is correct in particular cases, and secure thank to the fact that in D symbols are not implicitely re-exported. But this should not be the default case; then the plain fact of using a dedicated syntax (eg like proposed "import parseLib : *;") would deal as a kind of documentation about a non-standard practice.

Maybe it's only me (and a few other martian programmers) expecting code to be commonly clearer...


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 29, 2010
On Tue, 28 Dec 2010 16:01:24 +0000 (UTC)
"Adam D. Ruppe" <destructionator@gmail.com> wrote:

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

> That's a feature.
> [...]
> 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.

But that's true for explicite imports as well:
	import util	: x1,x2,x3;
-->
	import oauth	: x1,x2,x3;
What's the point? Or what do I miss?
(Name qualification in code, I mean at use place, is best used for std or third-party lib, I guess. When one uses their own utils, it's generally not useful; listing used symbols at import place is enough for reader documentation.)

denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 29, 2010
On Tue, 28 Dec 2010 18:37:37 +0100
Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

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

Waow! had not guessed that. Thanks for the tip :-) But it does not change anything relating to the 'feature' "import every symbol by default", as far as I can tell. Or what? On the contrary, it seems to me this helps & having good documentation practice and/or clearer code without potential refactoring issues. (Which anyway find&replace "foo." --> "foobar." solves is 99% cases.)

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 29, 2010
spir wrote:
> These are imo essential documentation needs unfulfilled.
> 1. at import place, does not tell the reader which symbols
> are actually used

Two things: a) Why does it really matter at all? The specific functions won't change the fact that you depend on the module.

b) Even if you grant that, what are the odds that such documentation
   would stay up to date as you remove the use of functions?

> 2. at use place, does not tell the reader where a given symbol
 is imported from

That introduces an unnecessary dependency on module implementation details (unnecessary in the strictest sense - it compiles fine without it!) If you want to determine that, you can consult the documentation or the compiler.

>  For instance, I commonly use this scheme: [snip std.file]

I do that too in a lot of cases, where the module name is meaningful and not redundant. Same for things like std.base64.encode, though the new release makes the module namespace redundant...

Anyway, in some cases it gives good information. But what about std.variant.Variant? Or std.json.toJSON? You're just repeating yourself, and if you want to know where it is from, you can check the imports and/or the documentation to easily confirm it: http://dpldocs.info/Variant

Listing the name over and over again, and having to seek and change it over and over again when other modules make a small change, just adds a lot of work for no benefit. Besides, if you want docs on the item, you have to look it up anyway.

> Maybe it's only me (and a few other martian programmers) expecting > code to be
commonly clearer...

You're assuming your way is, in fact, objectively clearer.
December 29, 2010
spir wrote:
> But that's true for explicite imports as well:
>  import util : x1,x2,x3;
> -->
>  import oauth : x1,x2,x3;

Yeah, that's not too bad, since the changes are still just in import lines. Though like I said in my last post, I don't see much of a benefit here either.

> (Name qualification in code, I mean at use place, is best used for std or third-party lib, I guess.

I don't see a difference between the two. If the source module matters, it matters if it is third party or not. If it doesn't matter, well it just doesn't matter!
December 29, 2010
On Wed, 29 Dec 2010 14:40:31 +0000 (UTC)
Adam Ruppe <destructionator@gmail.com> wrote:

> > (Name qualification in code, I mean at use place, is best used for std or third-party lib, I guess.
> 
> I don't see a difference between the two. If the source module matters, it matters if it is third party or not. If it doesn't matter, well it just doesn't matter!

Your argumentation seems to mix two (related but distinct) features:
* Explicite import at import place.
	import std.stdio        : writeln;
* Name qualification at use place
	std.stdio.writeln("foo");
(When you use the first one, the second one is facultative, indeed.)

I am argumentating for the first feature but your replies (and replies from others) often seem to imply I was preeching (which I don't, i'm not fan of Java at all). I think we both agree the qualification idiom is a good thing when it helps clarity, meaning names make sense.
It is wrong for me to make implicite import-all the default scheme. I cannot imagine how/why you do not see the huge documentation benefit of listing your toolkit at the top a module. Also, I do not understand how I'm supposed to guess where a func comes from if nothing in the importing module tells it to me. (Surely I sadly miss the seventh divination sense of talented programmers.)

About your reply above, when using your own toolkit or domain-specific module, semantic or conceptual relations with what your code is doing usually are more obvious:
	import myParsingLib :	Literal, Klass, Choice, Sequence;
	// expression parser
	auto dot = new Literal(".");
	auto digit = new Klass("0-9");
	...
Qualification would often be kind of over-specification.
This is different from using general-purpose libs like for handling files: here, proper qualifiers are often a true semantic hint on what an instruction copes with. Also names of funcs, types, constants, are often very similar (if not identical) in various general-purpose libraries. (Think at write*, read*,...)


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 29, 2010
On 12/29/10, spir <denis.spir@gmail.com> wrote:
> It is wrong for me to make implicite import-all the default scheme. I cannot imagine how/why you do not see the huge documentation benefit of listing your toolkit at the top a module. Also, I do not understand how I'm supposed to guess where a func comes from if nothing in the importing module tells it to me.

Like Walter said, D offers measures against function hijacking and that was the biggest problem with implicit import-all. D still offers features like static import and selective import, so if someone wants to take advantage of those to make the code more readable, they *can*.

In the end it's up to the programmer to choose. D has many options to
keep everyone happy.
Personally, I start writing some tryout code by importing modules as normal:

import std.range;
import std.algorithm;
{ ... test some code..
}

And then when I've figured out a good set of symbols that I need I add a colon and list them:

import std.range : retro;
import std.algorithm : reduce, reverse;

But I really don't see the benefit of changing the semantics of import. You won't get shot in the foot since D offers good protection from function hijacking.

But even if you did change the semantics of import to a static import, you still wouldn't fix the *programmers* themselves. Everyone will simply start using /import module.*/, or /import module.all/ instead of using the safe default /import module/ which would be a static import. No defined default or convention in the community can force programmers to code in one way.

And if you doubt that, just take a look at all the Python code that's out there. A ton of programmers still use the star syntax to import every symbol into the current scope, even though its frowned upon by the Python community (and afaik you can't use it anymore in Python 3). But D has counter-measures which prevent hijacking, and hence it's pretty safe to simply import the whole module without worrying too much.

As for figuring out where each symbol comes from, if it's your own codebase you'll probably use the colon syntax if you're really having problems figuring out where the symbols are coming from. Otherwise in larger codebases you'll more than likely use some capable IDE or a plugin that knows where each symbol comes from and lists them for you.

Otherwise I don't know what this discussion is about anymore. :)