Importing two libraries that export the same library: prefixed vs unprefixed

431 views
Skip to first unread message

Yegor Jbanov

unread,
Jun 6, 2013, 2:29:22 PM6/6/13
to mi...@dartlang.org
It looks like Dart handles correctly the situation when two prefixed imports export the same library:

lib/a.dart:
library a;
import "c.dart";
export "c.dart";
class A {}

lib/b.dart:b.dart:
library b;
import "c.dart";
export "c.dart";
class B {}

lib/c.dart:c.dart:
library c;
class C {}

bin/d.dart:
import "../lib/a.dart" as a;
import "../lib/b.dart" as b;

main
() {
 
var c1 = new a.C();
 
var c2 = new b.C();
 
print(c1 is b.C); // prints true
 
print(c2.runtimeType == c1.runtimeType); // prints true
}

So it recognizes that both a.C and b.C are the same class. However, without prefixes:

bin/e.dart:
import "../lib/a.dart";
import "../lib/b.dart";

main
() {
 
new C();  // Causes "ambiguous reference" error
}

It seems there's nothing ambiguous about symbol C. Am I missing something?

Yegor

Ladislav Thon

unread,
Jun 6, 2013, 2:36:26 PM6/6/13
to mi...@dartlang.org
There was a long discussion about this cca a week ago, see the thread "Problem: Multiple import of the same library". Long story short: never export a library that someone might import.

LT

Yegor Jbanov

unread,
Jun 6, 2013, 2:49:10 PM6/6/13
to mi...@dartlang.org
Agreed, as a matter of style I can see how this may lead to confusing code. At the moment, however, my interest is of a more technical nature. Is this really is an issue of ambiguity?

Gen

unread,
Jun 6, 2013, 4:31:57 PM6/6/13
to mi...@dartlang.org
I do not see how a proper working "export" could lead to confusing code.

Much more confusing is "import" that suggests to have a private library instance and not a shared one.

And "export" would be intuitive and useful if it could be considered to add a (publicly shared) library as complement to the exporting library.
The debate about what to put in dart:core and what not would also be less of a problem with a "correctly" working export mechanism.

I wonder why the clarification of the current "import" and "export" mechanism has not highest priority.

Iván Zaera Avellón

unread,
Jun 6, 2013, 4:38:28 PM6/6/13
to misc
Import and export mechanism is clarified. The problem is that we don't like it ;-).

Maybe Dart developers should finally accept that import/export is a total mess and think about ways to change it. Or at least tell us why it is like it is now and what are the advantages and what alternatives they have taken into consideration.

If not, we could do a brainstorming and see what we get, but I don't find the brainstorming useful if it not going to be taken into consideration or if it is too late to change import/export.




2013/6/6 Gen <gp78...@gmail.com>
--
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
 
 

Gen

unread,
Jun 6, 2013, 4:42:00 PM6/6/13
to mi...@dartlang.org
It is clarified by trial and error.
Not an ideal case.

Gilad Bracha

unread,
Jun 6, 2013, 5:05:38 PM6/6/13
to General Dart Discussion
I have the feeling I've explained this before, but I'll give it another go.

The basic idea is that it is the API exported by a library that matters. If you don't change that interface, your clients should never break.  If you start taking into account where a given function or class originated, that property no longer holds. 

For example, suppose you import function f() from two libraries:

library c;
import 'a.dart';
import 'b.dart';

var s = f();

...


if both a and b define function f(), we consider that to be ambiguous - *always*. Now suppose we have

library a;

f() => 91;

library b;
import 'a.dart';
export 'a.dart';

g() => 'abc';

You may argue that we can see that the same f() is imported into c and there is no problem. However, if b decides to implement its own version of f()

library b; // revised
import 'a.dart';

f() => 42;

The API exported by b has not changed, yet assuming we considered c legal before, c breaks now because there is a real ambiguity that b has no control over.

So it really is a lot cleaner to insist that clients disambiguate names that come in from different imports.  The truth is that the only robust practice is to always do that - either by prefixing or by explicitly restricting imports to elements you are actually using. Otherwise, if a library you use adds new members, things can break (independently of what approach we take the the specific issue raised in this thread).

Put another way, wildcard imports are convenient in the short run but do not scale. Other languages have discovered this the hard way. I think what should be done is to improve tools so they customize your imports intelligently, so you get

library c;
import 'a.dart' show f;
import 'b.dart' show g;

without having to type the show clauses yourself. This also means you can see exactly where your dependencies come from.  Once the tool does this for you, the inconvenience goes away and you are much better off. 

--
Cheers, Gilad

Gen

unread,
Jun 6, 2013, 5:30:27 PM6/6/13
to mi...@dartlang.org
Thanks for the answer.
The Dart tutorial should comprise your answer.
Explaining the same thing again and again to people like me might be a sign that something is not right.

I have no problem with that if library B exports library A that the elements of the exported A are no longer the elements of the library A when imported directly.
But at runtime all elements of library of A are the same again and shared. This is not just than tricky, this is betrayal.

That libraries do not just comprise immutable constants like types and functions but mutable variables makes sharing of libraries even worse. 

The error message shows perfectly the inconsistency.
Is the library A exported B shared and identified as the library A imported by C or not ? 
For the tools: No.
At runtime: Yes.

Yegor Jbanov

unread,
Jun 6, 2013, 5:32:20 PM6/6/13
to mi...@dartlang.org
The API exported by b has not changed, yet assuming we considered c legal before, c breaks now because there is a real ambiguity that b has no control over.

Ah, got it. Makes sense. Thanks!

Bob Nystrom

unread,
Jun 6, 2013, 5:36:55 PM6/6/13
to General Dart Discussion

On Thu, Jun 6, 2013 at 2:05 PM, Gilad Bracha <gbr...@google.com> wrote:
The API exported by b has not changed, yet assuming we considered c legal before, c breaks now because there is a real ambiguity that b has no control over.

I think the confusion here is that different people have different intuitions about what "API" means. Your definition is that b's API is just the bare identifier "f". If you take that as true, your reasoning about not allowing collapsing exports makes sense.

However, others in this thread feel that b's API isn't just the bare string "f", it's "f" along with the provenance of what library that name was defined in. If you follow that definition, then in your example, a breaking API change has occurred. Even though f's name is the same, it's provenance is different. If Dart took provenance into account (to allow multiple imports of the same exported name to not cause a collision error) then this would be a user-visible change to the API.

I think either model is valid and internally consistent. If Dart did allow multiple imports of the same exported name to coalesce without error, it just means we would have to keep in mind that moving a name from one library to another is a breaking change.

Personally, I prefer the model that Dart currently has even at the minor expense of occasional export collisions. I'll take that if it gives me the freedom to rearrange my libraries without breaking users.

Cheers,

- bob

Gen

unread,
Jun 6, 2013, 6:08:53 PM6/6/13
to mi...@dartlang.org
To me the object (type,...) bound to a symbol matters as much as the symbol.
The symbol and the bound object is what an API and libraries are all about in a typed language: Reuse objects defined elsewhere, not just symbols.

I do not know exactly what you mean by provenance.
That binding depends on the provenance which is the library in which the symbol is defined, right ?
That we use prefixes shows that provenance matters.
That the tools complain shows that provenance matters.

Am I blind that I do not see the obvious and that nothing is inconsistent in Dart ?
My error message: The element 'X' is defined in the libraries 'lib.dart' and 'lib.dart'

If the dart compiler gathers from all libraries the elements and checks for inconsistent definitions of e.g class "X" or function "f", then the error messages and library local prefixes make no sense.
A prefix for class X does not turn class X into class prefix.X.
Note: We have nominal typing and not structural typing.
Multiple classes "X" are all right because they are defined in different libraries (= provenances). 

Gen

unread,
Jun 6, 2013, 6:54:18 PM6/6/13
to mi...@dartlang.org
Of course c can break if b changes.

How did this insight change our world view with regard to ambiguities ?
I am not sarcastic.
I honestly want to understand as well.

Gen

unread,
Jun 6, 2013, 7:07:32 PM6/6/13
to mi...@dartlang.org
I mean, I know now how to use export and that prefixes are used for the special case that b defines its own class C or function f.
But why is this behavior so useful to be the default ?
Why not an error message in case things are actually ambiguous ?
And in Gilads example, why is the ambiguity error not in the exporting lib b that overrides f but instead in the importing library c that uses f from b ?

Gilad Bracha

unread,
Jun 6, 2013, 7:09:14 PM6/6/13
to General Dart Discussion
On Thu, Jun 6, 2013 at 3:54 PM, Gen <gp78...@gmail.com> wrote:
Of course c can break if b changes.

The point is it need not break if b's API is preserved.  We don't care who implemented things in b, we care what they do.  
--
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
 
 



--
Cheers, Gilad

Gilad Bracha

unread,
Jun 6, 2013, 7:12:09 PM6/6/13
to General Dart Discussion
On Thu, Jun 6, 2013 at 4:07 PM, Gen <gp78...@gmail.com> wrote:
I mean, I know now how to use export and that prefixes are used for the special case that b defines its own class C or function f.
But why is this behavior so useful to be the default ?
Why not an error message in case things are actually ambiguous ?
And in Gilads example, why is the ambiguity error not in the exporting lib b that overrides f but instead in the importing library c that uses f from b ?

b only exports a single f(). There is no ambiguity there. In contrast, c imports f() via two different imports.  That is ambiguous. You want to resolve the ambiguity by chasing down where f() originally came from. I've explained why this is a problem as best I can.  


--
Cheers, Gilad

Yegor Jbanov

unread,
Jun 6, 2013, 9:12:20 PM6/6/13
to mi...@dartlang.org
Of course c can break if b changes.
How did this insight change our world view with regard to ambiguities ?

What changed my world view is the concept of exported symbols becoming part of the imported library's interface. Even though b's implementation did change, it's interface stayed the same. In my original example the fact that the symbol C happens to refer to the same class is an implementation detail, and so it should not "leak" into the user code. If it did, it would restrict the authors of either of libraries a and b to change the implementation, and that would defeat the purpose of having an interface. Once you start thinking in terms of interfaces, the ambiguity becomes quite clear. C in a and C in b are different interface elements sharing a name, and therefore new C() is ambiguous. In fact, had I read the error message more carefully I might have got that sooner (the full file paths, stripped below, didn't help with readability):

'e.dart': Error: line 5 pos 7: ambiguous reference: 'C' is defined in library 'a.dart' and also in 'b.dart'

Cheers!

Justin Fagnani

unread,
Jun 6, 2013, 9:36:06 PM6/6/13
to General Dart Discussion
On Thu, Jun 6, 2013 at 6:12 PM, Yegor Jbanov <yjb...@google.com> wrote:

Of course c can break if b changes.
How did this insight change our world view with regard to ambiguities ?

What changed my world view is the concept of exported symbols becoming part of the imported library's interface. Even though b's implementation did change, it's interface stayed the same. In my original example the fact that the symbol C happens to refer to the same class is an implementation detail, and so it should not "leak" into the user code. If it did, it would restrict the authors of either of libraries a and b to change the implementation, and that would defeat the purpose of having an interface.

Currently we do leak the implementation to user code via the "is" operator and runtimeType, as shown by your example. I was somewhat surprised to see that thee lines:

  print(c1 is b.C); // prints true
  
print(c2.runtimeType == c1.runtimeType); // prints true
 
printed true.



Once you start thinking in terms of interfaces, the ambiguity becomes quite clear. C in a and C in b are different interface elements sharing a name, and therefore new C() is ambiguous. In fact, had I read the error message more carefully I might have got that sooner (the full file paths, stripped below, didn't help with readability):

'e.dart': Error: line 5 pos 7: ambiguous reference: 'C' is defined in library 'a.dart' and also in 'b.dart'

Cheers!

--
Message has been deleted

Joao Pedrosa

unread,
Jun 7, 2013, 2:28:09 AM6/7/13
to mi...@dartlang.org
Hi,

With regards to this topic, I quite like the way Dart works, even if I'm in favor of adding prefixes to the libraries of others that I use. I can use 2 letter acronyms when referencing other libraries which to me is good enough. If I had to use long names all the time, then I'd have a problem with it. :-)

Regarding what language is best to copy, it's not so much a case of language, but more a case of toolset. Dart is much more than just a language. And that's the big problem. Programmers can't settle on a small toolset, ever. It always grows as new versions are added. And that's also the problem. Because if tools keep growing big, it could be that they can't be standards. It's a costly luxury to keep growing a toolset when it's not getting any closer to becoming stable and a standard.

Then say, toolset designers have seen it all and want to keep the toolset small enough in order to improve its adoption as a standard. Then they could dream of adding features by extending the core functionality that's already a standard. In the case of a language, they'd rather add new libraries than to change the core of the language and tools all the time. That's their "dream." See for instance how Java barely evolved language-wise, as most things it acquired came thru new libraries.

So the question is whether folks should dream of creating standards or not. Shooting for standards is their carrot, their goal. It's their "aim high" strategy. It's the reason they might have open sourced it in the first place.

C#/.NET, Java, Objective-C, C/C++, and yes JavaScript, the world is full of standards. We can pick and choose to some extent. They say Dart is a chance to give us a choice to JavaScript. You may not believe it, but it's a dream.

This rambling is from seeing a lot of discussions on this list revolving around the shortcomings of Dart. To some extent, I agree that Dart falls short. I don't know what the alternative is though. Because whether or not we are shooting for a standard makes a huge difference.

Cheers,
Joao


On Fri, Jun 7, 2013 at 2:49 AM, mezoni <andrew...@gmail.com> wrote:
@Gen
Do not expect to get answers to very specific questions.
Of your questions and complaints will choose only those that are nice to answer.
Topical and reasonable questions will always remain unanswered.

Many people (developers) only react when they have something to say.
In other cases, they simply remain silent or choose simple and easy questions to answer them.

Usually this happens for several reasons. At least two of these are valid.
1. Silent when unpleasant to admit that this is not what others want.
2. Just no reasonable answers to reasonable questions.

When you long to explain the simple thing as did Gilad then I think that it's actually not that simple.
And perhaps no one needs. Because everyone else perceive it in other manner (simple than this want Gilad).

Sometimes coming up with a very interesting but totally useless things.
Why should anyone worry about me that if I do something to change that I will break compatibility.
There are other ways around this problem.
I do not understand why to avoid this problem is to "make a fuss"?

Something that can be used daily sacrificed of what will occur very infrequently?

Also my personal opinion about Smalltalk language.
1. Old language which is not used anywhere
2. Not to focus on the "dead" languages but adopt lively and popular. Such as Java and C #.
Message has been deleted

Iván Zaera Avellón

unread,
Jun 7, 2013, 3:58:51 AM6/7/13
to misc
Good. :-) 

So, with Gilad's explanation and the sentence "don't export libs that can be imported by anyone else" I think we have all directives for using import and export. Is that OK? Because I want to write an article in Spanish explaining this (I may also translate it to English if it is clear enough to add it to Dart's documentation and someone requests it).

I still have one doubt. If "don't export libs that can be imported by anyone else" is true, what is export for? I mean, is it an alternative to "part" for tighter visibility? Does it have implications for browser downloading, speed? In other words: when should I use part and when should I use import+export?



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

Ladislav Thon

unread,
Jun 7, 2013, 4:06:47 AM6/7/13
to mi...@dartlang.org
If "don't export libs that can be imported by anyone else" is true

It's an advice from someone (me) who is not a part of the Dart team, so you should take it with a grain of salt :-)
 
I mean, is it an alternative to "part" for tighter visibility?

That's the only safe usage of export that I know of. There are people who prefer to compose libraries from smaller libraries that are considered private (typically one library per file). In that case, export makes sense.

LT

Iván Zaera Avellón

unread,
Jun 7, 2013, 4:28:28 AM6/7/13
to misc
> It's an advice from someone (me) who is not a part of the Dart team, so you should take it with a grain of salt :-)

It's an advice which makes a lot of sense. I cannot see any other scenario where export can be used without danger.

>That's the only safe usage of export that I know of. There are people who prefer to compose libraries from smaller libraries 
>that are considered private (typically one library per file). In that case, export makes sense.

That's exactly what I had in mind so I suppose it confirms my assumptions about how import, export and part have to be used.




Iván Zaera Avellón

unread,
Jun 7, 2013, 4:31:55 AM6/7/13
to misc

@mezoni Where is that in the documentation? I googled for it and only find it in the book "Dart: up and running" :-o.



2013/6/7 mezoni <andrew...@gmail.com>
>> I still have one doubt. If "don't export libs that can be imported by anyone else" is true, what is export for? I mean, is it an alternative to "part" for tighter visibility? Does it have implications for browser downloading, speed? In other words: when should I use part and when should I use import+export?

Read Dart documentation.
1. You might have a huge library that you implement as a set of smaller libraries (that you and only you control).
2. You might create a library that provides a subset of methods from another library (that you and only you control).

Re-exporting libraries

You can combine or repackage libraries by re-exporting part or all of them. For example, you might have a huge library that you implement as a set of smaller libraries. Or you might create a library that provides a subset of methods from another library.


пятница, 7 июня 2013 г., 13:58:51 UTC+6 пользователь Iván Zaera Avellón написал:
Message has been deleted
Message has been deleted

Iván Zaera Avellón

unread,
Jun 7, 2013, 5:03:20 AM6/7/13
to misc
That's OK, I don't care either where it comes from but I couldn't find it. 


Thanks. I guess I will have to re-read it again.


2013/6/7 mezoni <andrew...@gmail.com>
>> @mezoni Where is that in the documentation? I googled for it and only find it in the book "Dart: up and running" :-o.

I do not knew about it. And I do not know now.
I do not care about the fact that this is a chapter from the book.
The main thing that this information offered on the official Dart site in the "docs" folder.
So it's documentation (I assume).

Gen

unread,
Jun 7, 2013, 5:20:05 AM6/7/13
to mi...@dartlang.org
@ to all:
Thank you for your answers. I know now how "export" is meant to work in Dart. 
Although I can not understand why the identifier matters but not what it actually stands for.

@ mezoni:
Thank for your sympathy.
have indeed the feeling that my lengthier explanations are not taken to account.
But I am already satisfied to know now what is intended to work.

@ Justin Fagnani:
Thank for your controvert comment as a team member.
Message has been deleted

Bob Nystrom

unread,
Jun 7, 2013, 12:07:32 PM6/7/13
to General Dart Discussion
On Fri, Jun 7, 2013 at 1:28 AM, Iván Zaera Avellón <iza...@gmail.com> wrote:
>That's the only safe usage of export that I know of. There are people who prefer to compose libraries from smaller libraries 
>that are considered private (typically one library per file). In that case, export makes sense.

That's exactly what I had in mind so I suppose it confirms my assumptions about how import, export and part have to be used.

That's right. When export was first being discussed for addition in the language, two scenarios came up:
  1. We wanted to split big libraries like "dart:html" into smaller libraries (webgl, webaudio, svg, etc.). At the same time, for users that wanted to use all of those features, some people thought it would be cumbersome to have to import each of those pieces. So the idea was that dart:html would be split into multiple libraries, but then there would be one mega-library that would export all of those pieces. If you want everything, just import the mega-library.

  2. For users (like me) that don't like "part", it lets you break a library into separate files while still exporting a single library interface to users.
(There may be other scenarios the designers had in mind, but those are the ones I heard.)

In practice, scenario 1 doesn't work with export's semantics, which is what this thread is about. Also, we haven't heard too many complaints about users having explicitly import multiple "dart:" libraries for all of the various pieces so it may not be a problem anyway.

For scenario 2, "export" works fine. I and others use it pretty frequently without any trouble. (In fact, most people probably haven't noticed that it's being used.)

Cheers!

- bob

Gen

unread,
Jun 7, 2013, 2:06:26 PM6/7/13
to mi...@dartlang.org
With the current implementation where libraries or at least their elements are shared, the real choice is between:
- scenario 1: A library is equivalent to the sum of the exported libraries augmented by its own elements.
- scenario 3: A library can override identifiers/elements of exported libraries without warning or error. 

Your scenario 2 is covered by scenario 1 and 3 and also by using "part of".

My use of scenario 1 is mainly scenario 2 except that I do not want to care about how many times I have imported explicitly or implicitly (via export) the same library.
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
0 new messages