"import as" best practices?

1,656 views
Skip to first unread message

James deBoer

unread,
Oct 22, 2013, 7:53:41 PM10/22/13
to mi...@dartlang.org
We have an open "to be discussed" issue in Angular.dart (#24) and would love the community's input:

What is the best practice for avoiding symbol collisions when importing libraries?  Especially collisions with core libraries.


For example, dart:async defines a class Zone.  So does Angular.  This situation is not a problem in Angular, since we use "import dart:async as async".  When we want to reference dart:async's Zone, we say "async.Zone".

This is great -- we like this method internally. It is clear which Zone we are talking about.  However, it has the potential to break Angular.dart customers:

If you have an app that imports both Angular and dart:async, e.g.

import "dart:async";
import "package:angular/core/module.dart"

The Dart compiler will complain that Zone is defined twice: once in dart:async and once in Angular.

==> One solution is to import Angular "import package:angular/angular.dart as ng".  Any references to Angular classes would need to be prefixed with "ng.".  "import as" is viral, if a library uses it, the customers of the library are forced to use it as well.

This requirement could be a good thing.  Library prefixes make everybody's code easier to understand.  If we agree that we should be using "import as" for all libraries, the symbol collision problems are solved as well.

However, there is no "export as" syntax in Dart.  If you are using exports to manage your imports, (e.g Angular tests) you are out of luck.

==> A second solution is to ban the use of "import as" from library code (such as Angular).  In an "import as"-free world, Angular can not have a Zone class when dart:async has one as well.  Library authors would need to be aware of collisions, perhaps by prefixing symbols (e.g. NgZone)

Library customers would not need to worry about name collisions between libraries and the Dart core.  There is still a chance that two independent libraries may collide, though.

In either case, we are still talking through this practice with respect to Angular.dart.  How are other libraries managing their imports?


Thanks,
James deBoer
Twitter/github: @jbdeboer




Gilad Bracha

unread,
Oct 22, 2013, 8:16:01 PM10/22/13
to General Dart Discussion
With imports, you have two ways to make your library robust against changes in your dependencies:

1. Always prefix (imports as) to avoid name collisions.
2. Always use show to explicitly control what is imported.

For exports, only the 2nd option makes sense. A prefix is internal to your library, and exporting under a prefix, even if it were possible, what not guarantee the absence of conflicts downstream.

The simplest advice is to always use show. That also has the benefit that you can tell at a glance what your using. However, if you have a very large set of imported names, a prefix may be more convenient. 

In any event, tools such as the Dart Editor (or any IDE) should assist in this, much as they do in Java, where people have learned that wildcard imports should be avoided.


--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.



--
Cheers, Gilad

Justin Fagnani

unread,
Oct 22, 2013, 8:45:38 PM10/22/13
to General Dart Discussion
On Tue, Oct 22, 2013 at 5:16 PM, Gilad Bracha <gbr...@google.com> wrote:
With imports, you have two ways to make your library robust against changes in your dependencies:

1. Always prefix (imports as) to avoid name collisions.
2. Always use show to explicitly control what is imported.

For exports, only the 2nd option makes sense. A prefix is internal to your library, and exporting under a prefix, even if it were possible, what not guarantee the absence of conflicts downstream.

The simplest advice is to always use show. That also has the benefit that you can tell at a glance what your using. However, if you have a very large set of imported names, a prefix may be more convenient. 

In any event, tools such as the Dart Editor (or any IDE) should assist in this, much as they do in Java, where people have learned that wildcard imports should be avoided.

The beginnings of this have landed: https://code.google.com/p/dart/issues/detail?id=12012
 


On Tue, Oct 22, 2013 at 4:53 PM, James deBoer <deb...@google.com> wrote:
We have an open "to be discussed" issue in Angular.dart (#24) and would love the community's input:

What is the best practice for avoiding symbol collisions when importing libraries?  Especially collisions with core libraries.


For example, dart:async defines a class Zone.  So does Angular.  This situation is not a problem in Angular, since we use "import dart:async as async".  When we want to reference dart:async's Zone, we say "async.Zone".

This is great -- we like this method internally. It is clear which Zone we are talking about.  However, it has the potential to break Angular.dart customers:

If you have an app that imports both Angular and dart:async, e.g.

import "dart:async";
import "package:angular/core/module.dart"

The Dart compiler will complain that Zone is defined twice: once in dart:async and once in Angular.

==> One solution is to import Angular "import package:angular/angular.dart as ng".  Any references to Angular classes would need to be prefixed with "ng.".  "import as" is viral, if a library uses it, the customers of the library are forced to use it as well.

I don't understand this statement, how is "import as" viral?

-Justin
Message has been deleted

Günter Zöchbauer

unread,
Oct 23, 2013, 1:12:04 AM10/23/13
to mi...@dartlang.org
what is
Message has been deleted

Günter Zöchbauer

unread,
Oct 23, 2013, 1:52:48 AM10/23/13
to mi...@dartlang.org

Iván Zaera Avellón

unread,
Oct 23, 2013, 2:33:57 AM10/23/13
to misc
Hi:

(Note: jump over the rant to get to the real info ;-))

<warning: rant>
Import and export where also discussed some time ago:


And I suppose in more threads, just to give you some of them.

I keep thinking the same: import/export is a mess in Dart and by version 2.0 we will have lots of "dart sucks" blog entries complaining about import and export. I suppose the answer will be "you are not using it right", but that won't matter because people will still get confused.
</warning: rant>


Apart from that, why do you use export? I mean, you should not use export unless you are exporting one of your "internal" libraries. And more important: you should never export "dart:XXX" packages because if you do that in your lib and I do that too in mine, nobody will be able to use both libs at the same time. I know you are doing it in tests, but IMHO it shouldn't be done in any place to avoid confusion to Dart beginners.

About prefixing classes with say Ng: just use "import as". Why should it be better to prefix class names with "Ng" than with "ng.", specially when the latter can be tweaked by the lib client with the "import as" statement?



2013/10/23 Günter Zöchbauer <gzo...@gmail.com>

--

Alexandre Ardhuin

unread,
Oct 23, 2013, 3:15:27 AM10/23/13
to General Dart Discussion
Assuming that "as" is used only to avoid name collisions, I wonder if it should be allowed to use names that are not in collision directly without prefix even if its library has been imported with "as".

For instance let lib "l1" constains 2 classes (A and B) and lib "l2" constains 2 classes (A and C). There's a collision for A so at least one of the imports should specify "as" to resolve that :

  import "package:l1/l1.dart";
  import "package:l2/l.dart" as l2;
  main(){
    A aFromL1;
    l2.A aFromL2; // prefix is mandatory because of name collision
    B b;
    C c;  // I can use C directly because there's no name collision
  }

In google_maps library I decided to use GMap instead of Map to avoid forcing users to use a prefix (because of Map in dart:core) and having that prefix everywhere in their code only for 1 name collision. With this new "as" behaviour only Map should be prefixed.

That way the code could be lighter.

What do you think ?

Alexandre


2013/10/23 Gilad Bracha <gbr...@google.com>
Message has been deleted

Justin Fagnani

unread,
Oct 23, 2013, 1:02:12 PM10/23/13
to General Dart Discussion
On Wed, Oct 23, 2013 at 12:15 AM, Alexandre Ardhuin <alexandr...@gmail.com> wrote:
Assuming that "as" is used only to avoid name collisions, I wonder if it should be allowed to use names that are not in collision directly without prefix even if its library has been imported with "as".

For instance let lib "l1" constains 2 classes (A and B) and lib "l2" constains 2 classes (A and C). There's a collision for A so at least one of the imports should specify "as" to resolve that :

  import "package:l1/l1.dart";
  import "package:l2/l.dart" as l2;
  main(){
    A aFromL1;
    l2.A aFromL2; // prefix is mandatory because of name collision
    B b;
    C c;  // I can use C directly because there's no name collision
  }

In google_maps library I decided to use GMap instead of Map to avoid forcing users to use a prefix (because of Map in dart:core) and having that prefix everywhere in their code only for 1 name collision. With this new "as" behaviour only Map should be prefixed.

That way the code could be lighter.

What do you think ?

You can basically already do this by importing the library twice, once prefixed and one un-prefixed, though that's an extra burden on users that I don't particularly like. It would look like this:

import 'package:google_maps/google_maps.dart' hide Map;
import 'package:google_maps/google_maps.dart' as maps show Map;

-Justin

Justin Fagnani

unread,
Oct 23, 2013, 1:35:57 PM10/23/13
to General Dart Discussion
On Tue, Oct 22, 2013 at 5:45 PM, Justin Fagnani <justin...@google.com> wrote:



On Tue, Oct 22, 2013 at 5:16 PM, Gilad Bracha <gbr...@google.com> wrote:
With imports, you have two ways to make your library robust against changes in your dependencies:

1. Always prefix (imports as) to avoid name collisions.
2. Always use show to explicitly control what is imported.

For exports, only the 2nd option makes sense. A prefix is internal to your library, and exporting under a prefix, even if it were possible, what not guarantee the absence of conflicts downstream.

The simplest advice is to always use show. That also has the benefit that you can tell at a glance what your using. However, if you have a very large set of imported names, a prefix may be more convenient. 

In any event, tools such as the Dart Editor (or any IDE) should assist in this, much as they do in Java, where people have learned that wildcard imports should be avoided.

The beginnings of this have landed: https://code.google.com/p/dart/issues/detail?id=12012
 


On Tue, Oct 22, 2013 at 4:53 PM, James deBoer <deb...@google.com> wrote:
We have an open "to be discussed" issue in Angular.dart (#24) and would love the community's input:

What is the best practice for avoiding symbol collisions when importing libraries?  Especially collisions with core libraries.


For example, dart:async defines a class Zone.  So does Angular.  This situation is not a problem in Angular, since we use "import dart:async as async".  When we want to reference dart:async's Zone, we say "async.Zone".

This is great -- we like this method internally. It is clear which Zone we are talking about.  However, it has the potential to break Angular.dart customers:

If you have an app that imports both Angular and dart:async, e.g.

import "dart:async";
import "package:angular/core/module.dart"

The Dart compiler will complain that Zone is defined twice: once in dart:async and once in Angular.

==> One solution is to import Angular "import package:angular/angular.dart as ng".  Any references to Angular classes would need to be prefixed with "ng.".  "import as" is viral, if a library uses it, the customers of the library are forced to use it as well.

I don't understand this statement, how is "import as" viral?

I'll expand on my question a bit and explain why "import as" is not viral.

First, prefixed imports are needed any time there are name conflicts, whether you're importing two libraries that define the same name, or a library that defines a name that you do as well. Whether or not one of those libraries has a prefixed import doesn't really affect anything. It has actually zero effect unless a user of your library also imports the library that you prefixed, and then they will have to resolve the collision by either prefixing your library or the other. But it's not viral: just because you prefixed an import, does not mean that your users always have to.

If you consider that prefixed imports let you deal with name collisions (of course) and therefore _encourage_ name collisions, then maybe you could say that the usage of prefixes sometimes necessitates more usage of prefixes. But it's really an issue of name collisions and users could just as easily use "show" and "hide" to manage that.

However, it's still, in my opinion, good practice to not require that your users have to think too much about this and I would recommend a few guidelines:

0. Choose whether you want your library to be designed for prefixing, if so document that it should be prefixed and ignore the rest. If you don't want to require prefixing then:

  1. Do not use a name in the dart:core library
  2. Preferably don't use names from the more common Dart core libraries especially dart:async and dart:html. This includes Zone :)
  3. Preferably don't use the same name for an implementation as it's superclass or interface. So don't so "class A extends a.A", since this means that users who need to use the superclass and implementation get a collision.
  4. Don't export names that will be imported other ways. This means not only should you only export names you own, but you should only export names that are part of "private" libraries (in lib/src).
  5. Carefully consider the number of names that a library defines - don't overly pollute your users namespace. You can hide implementation classes and function either by making them private, or even better, by putting them into an implementation library (better because they could still be imported if necessary).

-Justin
Message has been deleted
Message has been deleted

Alexandre Ardhuin

unread,
Oct 23, 2013, 2:38:44 PM10/23/13
to General Dart Discussion
I didn't know we can import a library twice.

Thanks Justin for this tip !


2013/10/23 Justin Fagnani <justin...@google.com>
Message has been deleted

Iván Zaera Avellón

unread,
Oct 24, 2013, 2:38:12 AM10/24/13
to misc
Are we really sure that this construction:

   import 'dart:async'; 
   import 'dart:async' as async;

doesn't cause any problem?

You are introducing all symbols of dart:async duplicated (at least it worked like this some time ago). Thus, you have Future and async.Future which, to the compiler, are two totally different classes (even though you know they are the same). 

For regular use cases this may work, but I'm not sure if mixing things from async.X and the raw dart:async will fail. For instance, say I have a lib named LIB, with classes A and B. Say A's constructor receives a B argument. And then I do:

import "LIB";
import "LIB" as L;

new L.A( new B );   <--- this used to fail compilation (but I'm not sure whether it should or not and whether it fails as of latest version)
new L.A( new L.B ); <-- this should work

If the first failure must hold, imagine this scenario with lots of interrelated classes. You may end up with runtime problems because you are mixing classes. In some way this is similar to randomly mixing classloaders in Java.

What do you think?



2013/10/23 <dangli...@gmail.com>
Just tested and found how to minimizing using prefix when name conficts.
Works without "show" and "hide" combinators.
Can be assumed as "import as" best practices.

Let' look into.

future.dart
+++++++++++++++++++++++++
library future;

import 'dart:async';
import 'dart:async' as async;
import 'future.dart' as this_library; // prefix itseld

class Future2 {
  Future2() {
    print('called new Future2() on future.dart');
  }

  Future2.withOnwFuture(this_library.Future future) {
    print('called Future2.withOnwFuture()');
    // Simulate we need class from 'dart:async'
    // It not conficts by name
    // Using it from 'dart:async' w/o prefix
    new Completer();
  }

  Future2.withAsyncFuture(async.Future future) {
    // Here we using 'dart:async' Future class
    // It conficts with own Future class
    // We using it with prefix (see method  parameter)
    print('called Future2.withAsyncFuture()');
  }
}

class Future {
  Future() {
    print('called new Future() on future.dart');
  }
}

+++++++++++++++++++++++++
test.dart

import 'dart:async';
import 'dart:async' as async;
import 'future.dart';
import 'future.dart' as future;

void main() {
  // use 'dart:async' with prefix
  var f1 = new async.Future(() => 41);
  // use 'future.dart' with prefix
  var f2 = new future.Future();
  // use 'dart:async' w/o prefix
  var c = new Completer();
  // use 'future.dart' w/o prefix
  var a = new Future2();
  // use 'future.dart' in both combinations
  var a1 = new Future2.withOnwFuture(new future.Future());
  var a2 = new Future2.withAsyncFuture(new async.Future(() => 41));
}

+++++++++++++++++++++++++
All works fine!
Using prefix are minimized only to where it reqired.

Lasse R.H. Nielsen

unread,
Oct 24, 2013, 4:13:00 AM10/24/13
to mi...@dartlang.org
On Thu, Oct 24, 2013 at 8:38 AM, Iván Zaera Avellón <iza...@gmail.com> wrote:
Are we really sure that this construction:

   import 'dart:async'; 
   import 'dart:async' as async;

doesn't cause any problem?

You are introducing all symbols of dart:async duplicated (at least it worked like this some time ago). Thus, you have Future and async.Future which, to the compiler, are two totally different classes (even though you know they are the same). 

They *should* be exactly the same classes. You are only importing one library (same URL => one library) and making its symbols available in two different ways.
That is: if you find a case where the compiler thinks these are different libraries introducing different classes, please file a bug!
 

For regular use cases this may work, but I'm not sure if mixing things from async.X and the raw dart:async will fail. For instance, say I have a lib named LIB, with classes A and B. Say A's constructor receives a B argument. And then I do:

import "LIB";
import "LIB" as L;

new L.A( new B );   <--- this used to fail compilation (but I'm not sure whether it should or not and whether it fails as of latest version)

It should not fail (if you add parentheses after B), and it doesn't when I test it in the VM, in dart2js or in the analyzer. I think the bug is fixed. :)
 
new L.A( new L.B ); <-- this should work

If the first failure must hold, imagine this scenario with lots of interrelated classes. You may end up with runtime problems because you are mixing classes. In some way this is similar to randomly mixing classloaders in Java.

What do you think?

Dart uses the URLs of libraries to distinguish them. If you import the same library (same URL) twice, you should just get different references to the same library and only one version of its declarations in the system.

/L



--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84
Message has been deleted

Iván Zaera Avellón

unread,
Oct 24, 2013, 4:42:13 AM10/24/13
to misc
It's OK. It just used to fail before, but it now seems to be fixed. Good.

Some time ago the compiler (or the editor) didn't recognize two libs with the same URL as the same lib. I didn't know if it was by design or a bug. Now I have clear that it is a bug.

Anyway, even if you can import the library twice, it would be (IMHO) a good addition to the language if the compiler could do it automatically, as proposed in dangling.feet's bug: https://code.google.com/p/dart/issues/detail?id=14342. It is somewhat cumbersome (and dirty) to import libs twice to get such useful effect.




2013/10/24 <dangli...@gmail.com>
>> Are we really sure that this construction:

   import 'dart:async'; 
   import 'dart:async' as async;

>> doesn't cause any problem?


It's not my problem. If something work wrong in Dart then I assume this is a problem of designers but not the my problem.
Or you think this prohibited by language specification?

import 'dart:async'; 
import 'dart:async' as async;

>> You are introducing all symbols of dart:async duplicated (at least it worked like this some time ago).
I think this can be called name isolation. If some names not used in some namespace then them must be assumed as not used.

I not looked how works optimizations in "mirrors" library. My be this is a problem, may be not a problem.
In all other cases this is not a problem.

P.S.
If the Dart compiler is an intellectual tool that for him the same name in different namespaces from the same library must be the same but not
different.
Also
duplicated means the same and can reduced.
What a problem?
Message has been deleted

Bob Nystrom

unread,
Oct 25, 2013, 1:38:13 PM10/25/13
to General Dart Discussion
On Tue, Oct 22, 2013 at 4:53 PM, James deBoer <deb...@google.com> wrote:
What is the best practice for avoiding symbol collisions when importing libraries?  Especially collisions with core libraries.


For example, dart:async defines a class Zone.  So does Angular.  This situation is not a problem in Angular, since we use "import dart:async as async".  When we want to reference dart:async's Zone, we say "async.Zone".

Here's my personal recommendations:

  1. Don't create names that clash with core libraries. While the Dart language has mechanisms to handle this case, it's still no fun for your users. When they see "Zone" they will assume it's the Zone they know and love in the core libraries and be confused when it isn't.

    You, of course, can't predict what names will be added to core libraries later, but I think it's generally a bad practice to knowingly collide with a "dart:" name.

  2. Avoid creating names that are likely to collide. I try to create top-level names that are pretty precise (but not necessarily verbose) and mention the domain of the code. The fact that you can handle colliding names doesn't mean you shouldn't think about ambiguity. It's still a chore and makes code harder for humans to read when you have lots of collisions.

  3. Consider designing your library to be imported with a prefix. If a library exposes more functions than types, it may be best to keep their names short and encourage users to import with a prefix. For example, the path package works best with a prefix.

    However, type annotations look weird with prefixes. If a library mostly exposes types, I try to avoid this.

  4. If I import two names that collide and I only use one, I just hide the other. Like:

    import 'libA.dart';
    import 'libB.dart' hide Collides; // not using this one

  5. If I import two names that collide and I use both, I import the less-frequently used one with a prefix. Like:

    import 'libA.dart';
    import 'libB.dart' hide Collides; // for other names in libB
    import 'libB.dart' as libB; // to access libB.
    Collides
These rules are pretty simple, if a bit soft, and have worked well for me for a while.

Cheers!

- bob

Reply all
Reply to author
Forward
0 new messages