Here's a raw idea - we discussed something similar before, but I think prior proposals missed one simple variant..The following fragment is taken from Lasse's original proposal. Exact syntax doesn't matter though.import dart.platform == "browser" : "some uri.dart"|| dart.platform == "standalone" : "other uri.dart"|| "default uri.dart"deferred as foo show x hide y;I think the mistake here is an attempt to create a union of different libraries, which most likely have incompatible interfaces.Instead, we better keep them separate by writing separate import statements and assigning different name to each library variant:import "some uri.dart" as foo when dart.platform=="standalone";import "other_uri.dart" as bar when dart.platform=="browser"When we work in IDE, analyzer will (internally) load all library variants.In the code, access to the stuff coming from conditionally imported library should be guarded byif (imported(foo)) { // "imported" is an intrinsic function
// use foo.anything}...if (imported(bar)) {// use bar.anything}Unguarded access is an error.
Naturally, when compiler really generates the code, it ignores all libraries that don't meet import conditions, and removes corresponding (dead) parts of code.However, in IDE, every feature continues to work, because all variants are loaded (internally), .Any counterexamples?BTW, the idea is quite simple, maybe it was considered and rejected - I'd like to know why.
@Erik: If I understand you correctly, the main concern is that the proposed solution doesn't "scale" well for N variants where N is large. I agree with that. But if typical value of N is 2, we don't care much. And 2 is exactly what we have in the main motivating example (browser vs standalone).
But what is the alternative? Implement common denominator?
If libraries were not designed to conform to common "interface" (I'm using the term loosely), is it realistic to be able to shoehorn them into common interface, even for N=2?
And in a (theoretic) case of larger N - what happens with common denominator? It might quickly become empty, so we end up with having to define a kind of "union" (instead of denominator) where some methods in concrete variants will just throw "not implemented". On top of that, slight differences in underlying libraries can make even common methods incompatible. And/or it becomes exponentially more difficult to define such common interface to begin with. Etc...
Again, this is all "in theory". In practice, we may not have large N.
> ... you can always break the encapsulation semantically, e.g., by implementing a toplevel `isBrowser` function
> ...But you _can_ get a high degree of encapsulation if you try. ;-)
Well... you try, then try harder, then you have terrible headache and decide to resort to "isBrowser" logic, and now all your critique applies anyway, just one step down the road?
> Conversely, do you really think that it's good from a software engineering point of view to avoid shoehorning entirely, i.e., to let every little configuration dependent quirk be visible in your canonical library?
No, God forbid!
I definitely see your point. I am just not sure how it will work in practice.
Maybe I should have asked more questions before proposing anything :)
E.g. there's a question of "library interface". What is it? Language doesn't define such thing.
Library cannot say "I'm implementing interface Foo".
So compiler will somehow derive "library interface" by analyzing alternatives in "import" statement? How exactly? What if some top-level definitions are present in one, and missing in another? Or have different signatures? Is it an error? Otherwise, how IDE will compute suggestions and detect errors?
Finally, if "library interface" gets defined, then we might want to treat those interfaces generically, as first-class constructs (e.g. we could pass references to libraries around etc). The thing is: right now, it all looks like a riddle. "Library interface" is either a worthy notion, or it is not. If it is, then we have to define it before conditional import (which is just an application of a concept).
Went through proposals/comments. The impression is that people can't even agree on a problem we are trying to solve.
Some want configurable implementation of known interface. Others want to just selectively load libraries which they need in particular configuration, without assumption of common interfaces (e.g. when run as standalone, I want library X, otherwise some completely different Y, or even nothing at all).
Unfortunately, I can't find any mention of major use case that immediately comes to mind: Suppose dart wants to support configurable drivers - e.g something similar to JDBC drivers. Not sure any current proposal deals with the situation. Is it a different problem, or a variant of the same? Maybe if we solve it, it automatically solves other problems, too?
BTW, with JDBC, it's generally impossible to specify the libraries statically. Implementations are loaded dynamically via Class.forName() and become part of infrastructure; particular implementation is chosen based on url prefix. Maybe it makes sense to generalize this setup and see where it leads? Dart has to address this problem sooner or later anyway, why not now?
NOTE: there's no assumption that in particular run, we want just one of N drivers - in fact, we may need a subset of them.
Went through proposals/comments. The impression is that people can't even agree on a problem we are trying to solve.Some want configurable implementation of known interface. Others want to just selectively load libraries which they need in particular configuration, without assumption of common interfaces (e.g. when run as standalone, I want library X, otherwise some completely different Y, or even nothing at all).
Unfortunately, I can't find any mention of major use case that immediately comes to mind: Suppose dart wants to support configurable drivers - e.g something similar to JDBC drivers. Not sure any current proposal deals with the situation. Is it a different problem, or a variant of the same? Maybe if we solve it, it automatically solves other problems, too?
@Bob: sorry I received your message after I hit "send", but it seems that we don't even need "library interface" or anything.Simple concept of "driver" plus simple conditional import (as defined above) can take us a long way.
// http.dartclass Http {}// http_io.dartimport 'dart:io' as io;class Http {io.HttpClient _client;...}// foo_html.dartimport 'dart:html' as html;class Http {html.HttpRequest _request;...}// main.dart// (made up syntax...)import 'http.dart'if (dart.io) 'http_io.dart'if (dart.html) 'http_html.dart';class MyHttp extends Http {...}
On Mon, Aug 31, 2015 at 9:55 AM, Alex Tatumizer <tatu...@gmail.com> wrote:@Bob: sorry I received your message after I hit "send", but it seems that we don't even need "library interface" or anything.Simple concept of "driver" plus simple conditional import (as defined above) can take us a long way.Yes, that's definitely one of the ideas that's floated around. Basically like a "deferred" import, but synchronous.It is the simplest of all of the proposals, but also the most limited. In particular, it means you cannot extend a configuration-specific class.The interface library proposal lets you do stuff like:// http.dartclass Http {}// http_io.dartimport 'dart:io' as io;class Http {io.HttpClient _client;...}// foo_html.dart
import 'dart:html' as html;class Http {html.HttpRequest _request;...}// main.dart// (made up syntax...)import 'http.dart'if (dart.io) 'http_io.dart'if (dart.html) 'http_html.dart';class MyHttp extends Http {...}Here, when you run the program on the standalone VM, MyHttp will have a field storing a dart:io HttpClient. On the browser, it will have an HttpRequest.
It appears that I gave up too soon. I looked into definitions of HttpClient and HttpRequest in dart API - these classes are completely different. @Bob: If you want to demonstrate the virtues of your idea, you better find another example.
I already understand that. But do they expose specific methods, too? E.g., onProgress, authenticateProxy, badProxyCertificateCallback and other 20 methods and properties that are present in one variant and missing in another?
If not, the thing is not usable: in the next version, you might need "onProgress" while running in browser, and there's no such method in the common interface. If you do expose them, the abstraction is very leaky, you need a lot of "isBrowser" logic, so we are back to square 1.
Interestingly, the argument I'm trying to make was already submitted as an issue: https://github.com/munificent/dep-external-libraries/issues/1
Now, the whole story looks like this: somebody asked to support configuration-specific imports. Very modest request.
And it's the SAME argument.
Instead, your proposal deals with a problem: to support configuration-specific import of a variant of common interface obtained by discarding all functionality present on one platform and absent on another (which, BTW, can lead to empty common interface, but it's beside the point).Now someone objects that it doesn't address the original issue of importing configuration-specific library. You objection is: "Sure, but look at this nice "extend" over here!!!"