Discussion:
[dart-misc] simple idea for configured imports
tatumizer-v0.2
2015-08-28 03:11:53 UTC
Permalink
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 by
if (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.
--
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+***@dartlang.org.
'Erik Ernst' via Dart Misc
2015-08-28 07:50:31 UTC
Permalink
Post by tatumizer-v0.2
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
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 by
if (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.
This approach is indeed pleasantly simple to explain.

But I think the proposal has one crucial problem: It requires the canonical
library (the one that contains the configurable `import .. || .. || ..`
directive) to explicitly depend on all the possible configurations, and it
causes the configuration dependent pieces of code to be completely tangled
into each other. You'd have to edit many bits and pieces of that library to
make it work again if you add an extra configuration.

Because of that, I'd prefer an approach where each configuration dependent
library ('some_uri.dart' and 'other_uri.dart') must conform to a common
library signature (different proposals have different notions of 'library
signature'), and the canonical library relies on that signature in order to
use the configuration dependent features. This will allow for a high
degree* of encapsulation of the configuration dependencies, and it is one
of the well-established principles in software that encapsulation based on
shared and explicit interfaces/signatures/contracts is good for
comprehensibility, maintainability, reusability, you-name-it-ility.

* Footnote: Of course, you can always break the encapsulation semantically,
e.g., by implementing a toplevel `isBrowser` function that returns true
with 'other_uri.dart' and false with 'some_uri.dart', and then do different
things in canonical code based on that. But you _can_ get a high degree of
encapsulation if you try. ;-)

best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
Benjamin Strauß
2015-08-28 08:10:07 UTC
Permalink
It's really a shame that we don't have first class classes (or rather top
level classes as libraries). That would simplify the whole subject. I have
nothing against these proposals, but to me it looks like all these
solutions will just clutter up the language and make it more complex for
the developers. :(
Post by 'Erik Ernst' via Dart Misc
Post by tatumizer-v0.2
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
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 by
if (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.
This approach is indeed pleasantly simple to explain.
But I think the proposal has one crucial problem: It requires the
canonical library (the one that contains the configurable `import .. || ..
|| ..` directive) to explicitly depend on all the possible configurations,
and it causes the configuration dependent pieces of code to be completely
tangled into each other. You'd have to edit many bits and pieces of that
library to make it work again if you add an extra configuration.
Because of that, I'd prefer an approach where each configuration dependent
library ('some_uri.dart' and 'other_uri.dart') must conform to a common
library signature (different proposals have different notions of 'library
signature'), and the canonical library relies on that signature in order to
use the configuration dependent features. This will allow for a high
degree* of encapsulation of the configuration dependencies, and it is one
of the well-established principles in software that encapsulation based on
shared and explicit interfaces/signatures/contracts is good for
comprehensibility, maintainability, reusability, you-name-it-ility.
* Footnote: Of course, you can always break the encapsulation
semantically, e.g., by implementing a toplevel `isBrowser` function that
returns true with 'other_uri.dart' and false with 'some_uri.dart', and then
do different things in canonical code based on that. But you _can_ get a
high degree of encapsulation if you try. ;-)
best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
tatumizer-v0.2
2015-08-28 16:27:29 UTC
Permalink
@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?

@Lasse: yeah, "error on unguarded access" was not a good idea, I take it
back :)
And I forgot about "deferred", it's really painful to think about
combination of deferred and conditional import.

Anyway, my point is that there might be a simple solution that covers some
common use cases. For alternative more complicated solutions nothing much
can be said at the moment, except that they are more complicated :)
--
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+***@dartlang.org.
'Erik Ernst' via Dart Misc
2015-08-28 17:31:23 UTC
Permalink
Post by tatumizer-v0.2
@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).
The main point is encapsulation and well-defined interfaces, which doesn't
take a large N to matter. It does get even worse with a large N because the
tangled code gets even more tangled, but already two variants give rise to
code where two mindsets must be kept ready-to-mind at all times. I do think
that the quality of the thinking and of the resulting software will be
higher if the amount of tangling (syntactically and semantically) can be
kept down.

But what is the alternative? Implement common denominator?
Create an abstraction (library signature) which is powerful enough to
enable the configuration independent code to be expressed, and then
implement that interface in each configuration dependent setting. It's a
common denominator, and it's the lowest one that will work, but no lower
than that.

Abstraction often yields more concise and readable code, but sometimes it
will give rise to duplication (a method body `doSomething(); if (isBrowser)
{../*1*/..} else {../*2*/..}` becomes `doSomething(); ../*1*/..` in the
browser variant and `doSomething(); ../*2*/..` in the other variant, which
duplicates `doSomething()`). But then we'll just have to use standard
techniques for eliminating code duplication (e.g., we could have a
configuration independent method calling `doSomething(); m(..);` where `m`
is a new method whose implementation is configuration dependent,
effectively `../*1*/..` resp. `../*2*/..`, depending on the chosen variant).

That's basically a matter of introducing more abstraction, such that the
parts which are actually configuration independent are allowed to be so
also technically. I wouldn't be surprised if the resulting software turned
out to have a better structure than it used to have.

If libraries were not designed to conform to common "interface" (I'm using
Post by tatumizer-v0.2
the term loosely), is it realistic to be able to shoehorn them into common
interface, even for N=2?
I do think that the cases where you can just write `if (isConfig1) {..}
else if (isConfig2) ..` will be rather easy to improve such that the
canonical code is highly configuration independent, and even when you do
need to do a bit of shoehorning it's for a good purpose.

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?

And in a (theoretic) case of larger N - what happens with common
Post by tatumizer-v0.2
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...
If no abstraction (toward a common library signature) is possible then you
simply have N completely different programs. No need to try to shoehorn
them into the same codebase. ;-)

Again, this is all "in theory". In practice, we may not have large N.
Post by tatumizer-v0.2
... 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?
I expect that such an `isBrowser` condition in the code would be rare,
especially if the code has been structured well. ;-)

best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
tatumizer-v0.2
2015-08-28 19:39:20 UTC
Permalink
Post by 'Erik Ernst' via Dart Misc
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).

Thanks,
Alex
--
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+***@dartlang.org.
'Erik Ernst' via Dart Misc
2015-08-28 20:26:43 UTC
Permalink
Post by tatumizer-v0.2
Post by 'Erik Ernst' via Dart Misc
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!
;-)
Post by tatumizer-v0.2
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.
I'm sorry, I should have defined some terms more explicitly (there are
several places where similar topics are being discussed). There is no
notion of a library interface in Dart, but I'd argue that it should be
considered, some approach to that issue should be chosen (for me,
preferably: library signatures should be well-defined, simple, and
explicit), and then that should be the basis for the treatment of
configuration dependencies.

A very simple approach would be to say that a library interface is a set of
toplevel non-type entities, typed with types that are available from
outside the configuration dependent libraries. (That's what I have proposed
for a long time; see
https://github.com/eernstg/dep-configured-imports/blob/master/DEP-configured-imports.md
).

Another approach would be to say that a library signature is (essentially)
a library S that contains no implementation. Any library L that has an
implementation "but is reduced to S when the implementation is erased"
conforms to S. It's a long, long, long discussion whether or not that's
simple. ;-)

You could of course also introduce an entirely separate notion of a library
signature as in SML for structs and signatures, where functors create the
need for first class libraries such that explicit signatures are
unavoidable.

But nobody is really in love with the idea that we'd bury Dart in a lot of
extra typing machinery in order to get support for `dart:io` and
`dart:html` in a shared piece of code. ;^)

Library cannot say "I'm implementing interface Foo".
It could, if we were to make those interfaces explicit.
Post by tatumizer-v0.2
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?
That's also a very long discussion. Try to check out
https://github.com/dart-lang/dart_enhancement_proposals/issues for other
proposals.

My motivation for preferring a no-types-exported approach is exactly that
any kind of structural equivalence that would make types exported from one
variant interchangeable with types exported by another variant is an alien
element in a language where type equivalence is otherwise nominally based.
Without that element it's much simpler. Surprisingly, you can still do a
lot. ;)

Finally, if "library interface" gets defined, then we might want to treat
Post by tatumizer-v0.2
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).
Sure, but that's not for free: The complexity of the language and the
conceptual framework that a programmer would need to build around the
language and its type system would go up, and not trivially so...

best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
tatumizer-v0.2
2015-08-29 15:18:24 UTC
Permalink
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.
--
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+***@dartlang.org.
tatumizer-v0.2
2015-08-31 02:12:58 UTC
Permalink
@Erik:
Upon more thinking: maybe JDBC interface provides a universal template for
the solution?
The design has a shape of lab flask with narrow neck and wide body.
Initially, we retrieve Connection object. Everything else we get from it
(there's a lot of stuff there). No public constructors - instead, requests
to Connection are used to create/retrieve objects.

It would be much more difficult to implement (or even understand) were it
structured differently.

If the pattern is universal (hard to prove, but it very well might be the
case), this simplifies things a lot IMO.
--
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+***@dartlang.org.
'Erik Ernst' via Dart Misc
2015-08-31 09:17:36 UTC
Permalink
Post by tatumizer-v0.2
Went through proposals/comments. The impression is that people can't even
agree on a problem we are trying to solve.
I think you are right that the problem(s) might have been stated more
explicitly in those proposals. But one main motivation is to enable
cross-platform software, that is, code that is configuration-independent,
but capable of using (importing) configuration-dependent features such as
'dart:io' and 'dart:html' to solve "the same problem" in two different ways.

It's easy for two different programs to import the same library, which
means that configuration dependencies can easily be expressed at the
beginning of the dependency graph (from "main" and onward to its imports),
but it is impossible (without some type of configurable imports) to have
configuration independent code at the beginning of the dependency graph and
configuration dependent code further down. Cross-platform software is an
example of this.

Some want configurable implementation of known interface. Others want to
Post by tatumizer-v0.2
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).
Different language mechanisms, same purpose.

Unfortunately, I can't find any mention of major use case that immediately
Post by tatumizer-v0.2
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?
You might very well want to create a product line of slightly different
programs where the choice of database driver is one of the dimensions of
configuration.

However, that particular example doesn't actually require configurable
imports: If you have a set of `...DatabaseDriver` classes and make the
choice among them using a `myDatabaseDriver = new SomeDataBaseDriver()`
somewhere in your code and then use `myDatabaseDriver` throughout your
program (suitably hidden behind some nice abstractions), then you would be
able to use one of many implementations of the same interface. You could
also make that choice dynamically, and you could use several different
implementations at the same time. If you want to avoid using space in your
runtime for all those variants then you can access them as `deferred`
libraries.

So, even though you might want to use configurable imports also in this
case, it's not one of those cases where configurable imports are required.
In general, configurable import is not required if the code that you wish
to use has been made available as variants (subtypes) of a given, shared
abstraction.

They are required in the case where certain libraries are available only in
some cases (e.g., only on some platforms: browser, vm, sky, ..), because
then your program just won't compile if your source code contains an
`import` (deferred or not) of the complete set of variants.

You could provide support for 'dart:html' on the vm or 'dart:io' in the
browser (this is indeed one of the proposals for configurable imports: just
make sure everything is available everywhere, even if every use of
'dart:html' in the vm might throw), but that isn't very scalable. Hence the
other proposals.

BTW, with JDBC, it's generally impossible to specify the libraries
Post by tatumizer-v0.2
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?
Dart does not support dynamic loading in an isolate (you can start a new
one on new code), presumably because it is crucial for space efficiency
that the complete set of dependencies in a program can be assessed by the
compiler (in particular, dart2js) such that "tree shaking" can be performed
and dead code eliminated.

But if you have a finite and known list of variants, you can still specify
them as `deferred` imports and get the choice at runtime as well as the
tree shaking.

NOTE: there's no assumption that in particular run, we want just one of N
Post by tatumizer-v0.2
drivers - in fact, we may need a subset of them.
`deferred` imports wouldn't have a problem with that, either.

best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
tatumizer-v0.2
2015-08-31 16:35:00 UTC
Permalink
If you have a set of `...DatabaseDriver` classes and make the choice
among them using a `myDatabaseDriver = new SomeDataBaseDriver()` somewhere
in your code and then use `myDatabaseDriver` throughout your program

That's exactly my point! A large class of use cases can be trivially
reduced to this one!

Let's recall where we started.

Suppose we have 2 libraries X and Y that don't implement the same
interface, but we think it's possible to find common denominator and
abstract away all (or most) of the differences.
In which case we still need to write wrappers (X1 and Y1 respectively) that
implement "common denominator" interface.

How do we structure the implementations of X1 and Y1? This can be done in
different ways, but the simplest one is to follow the "driver" patters: X1
and Y1 each expose a single object (e.g. "driverInstance" - a singleton),
which implement common interface (defined elsewhere) - and nothing else.
All top-level methods of libraries X and Y now become methods of driver
instance. In practical terms of usage, it doesn't make much difference.

E.g. where you previously used to write "var st=new
xlib.PreparedStatement()", you now write "var
st=xdriver.newPreparedStatement()" - syntactically, it looks just as a
different format of constructor invocation. After "new" becomes optional,
even this difference (in principle) can be eliminated: instead of "var
st=xlib.PreparedStatement", you write "var st=xdriver.PreparedStatement" -
the latter is just a function call that pretends to be constructor call
(not sure this is a good style, but certainly a possibility).

Now, it all plays very nicely with conditional import (the only proposed
earlier in this thread).

import "x1" when platform=="x" as x1lib;
import "y1" when platform=="y" as y1lib;
...
CommonDenominatorDriver myDriver=(x1lib.imported? xlib1.driverInstance:
ylib1.driverInstance);

Similarly, if conditions of imports are not mutually exlusive (e.g. we may
have N different drivers on one platform, and M different drivers on
other), we build a map:

var myDrivers={};
if (x1lib.imported) myDrivers["x1"]=x1lib.instance;
if (y1lib.imported) myDrivers["y1"]=y1lib.instance;

(This can be further improved to make initialization of the map automatic,
like JDBC does. But the point is clear already).

So we covered a case when 2 libraries CAN be shoehorned into same interface
Which leaves us with the case when we need to conditionally import xlib on
platform X, and ylib (or even nothing) on platform Y.
Then you don't have to redesign anything, just use conditional import as
above (with different library names), and then check "if (xlib.imported)"

This all doesn't require neither definition of "library interface" (which
would be difficult anyway), or even the creative use of "external" (as per
Bob's idea). Nothing but the simplest form of conditional import that just
adds "when" clause to existing syntax, that's it.

How about that?
--
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+***@dartlang.org.
Alex Tatumizer
2015-08-31 16:55:07 UTC
Permalink
@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.
What would be good though is to have language feature like "static" block
in java which automatically executes some library code on load. We can do
without it, but it would be just nicer to have it.
--
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+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2015-08-31 20:43:21 UTC
Permalink
Post by Alex Tatumizer
@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.dart
class Http {}

// http_io.dart
import '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.

Cheers!

- bob
--
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+***@dartlang.org.
tatumizer-v0.2
2015-09-01 03:17:56 UTC
Permalink
In particular, it means you cannot extend a configuration-specific class.
Sure, this is a valid argument, but it's not a very strong one IMO. You can
always wrap another object around driver instance - e.g. using delegation.
Not a big deal. There's lots of situations where you may need to do it
anyway. E.g. library method returns result of type IceCream, but in your
application, you never want plain IceCream - you want FancyIceCream
instead. What can you do about it? Use wrapper::
var fcream=new FancyIceCream(foo.getIceCream());

It's all subjective, of course, but in reference frame where I live, you
need 100 arguments like this to beat simplicity of driver pattern.
Anyway, I think I made my point, let's move on :-)
--
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+***@dartlang.org.
'Erik Ernst' via Dart Misc
2015-09-01 12:18:49 UTC
Permalink
On Mon, Aug 31, 2015 at 10:43 PM, 'Bob Nystrom' via Dart Misc <
Post by 'Bob Nystrom' via Dart Misc
Post by Alex Tatumizer
@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.
// http.dart
class Http {}
// http_io.dart
import 'dart:io' as io;
class Http {
io.HttpClient _client;
...
}
// foo_html.dart
I guess that's intended to be 'http_html.dart'.
Post by 'Bob Nystrom' via Dart Misc
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.
[Just to make sure that nobody will run out of arguments against my
preferred solution, here's a case that is hard to cover without `extends`
on configuration dependent classes ;-D ]

Basically, the easy work-around that you could use if you _don't_ have
support for subclassing a configuration dependent class is forwarding (that
is, almost-delegation): You create a new class `MyHttpBasedOnForwarding`
that implements whatever needed (presumably `implements Http` will do), and
then you obtain a configuration dependent object (`new Http()`, evaluating
to an instance of "http_io.Http" respectively "http_html.Http", depending
on the configuration), store it in a private instance variable, and forward
requests to it, adding whatever modifications you might need.

However, this setup has the well-known "SELF problem" (Lieberman, OOPSLA
1986: 'Using Prototypical Objects to Implement Shared Behavior in Object
Oriented Systems'), which means that it breaks as soon as there is a "self
send". For instance, if we wish to add logging (wow, new idea ;-) to a
method implemented by the configuration dependent `Http` classes, then we
can log the direct calls, but we will miss out on the internal calls on the
configuration dependent instance. The obvious remedy would be to have a
real inheritance relation rather than a forwarding relation.

...

Thinking about this once more, we would actually have a solution in the
'sub_canonical.dart' approach that I've already advocated a few times
(where the client would create a subclass of each configuration dependent
class in a configuration dependent setting, and deliver instances in a
configurably imported manner).

With that approach, you would simply need to share some material among the
configuration dependent subclasses, and that could nicely be done by mixin
application. The mixin would be configuration independent, and its
implementation would be available for each of the configuration dependent
settings by mixin application (or just some of them, if not every one will
work --- which is a case that direct support for inheritance from "any
class in a set of configuration dependent classes" would not be able to
handle).

We'd get real inheritance rather than forwarding, we get simple inheritance
rather than magic near-dynamic mixin application, and it's an approach that
any client could use if they need it.

[So maybe that didn't help: it wasn't such a hard case after all ;-) ]

best regards,
--
Erik Ernst - Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 KÞbenhavn K, Denmark
CVR no. 28866984
--
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+***@dartlang.org.
tatumizer-v0.2
2015-09-02 03:36:10 UTC
Permalink
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. It's easier to find common interface between the Horse the
the Cow than between these 2 classes. Common denominator will have to throw
away so many fine details that remaining definitions will not be good for
anything.

Which reminds me an old anecdote about math professor who defined a class
of objects axiomatically and throughout his career, proceeded to prove many
wonderful theorems about said objects - until, many years later, somebody
showed that the axioms are contradictory to begin with, so the class is
empty.

I'm back to my proposal - which I restate here in a condensed form:
1) add "when *condition*" clause to "import" statement
2) add method "imported()" for a library

What not to love here?
--
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+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2015-09-02 15:46:38 UTC
Permalink
Post by tatumizer-v0.2
It appears that I gave up too soon. I looked into definitions of
HttpClient and HttpRequest in dart API - these classes are completely
better find another example.
No, this is a real-world example. It's exactly what the http package does.

I just didn't clarify the details. The idea is not that you would extend
HttpClient and HttpRequest. It's that you can define your *own* Http
classes, one that wraps HttpClient and one that wraps HttpRequest. Those
Http classes expose a unified interface.

The interesting bit is that if you extend Http class, at runtime your
superclass is the actual configuration-specific Http wrapper class for that
platform.

Your proposal, and other similar ones can't express that.

- bob
--
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+***@dartlang.org.
Alex Tatumizer
2015-09-02 17:20:27 UTC
Permalink
It's that you can define your *own* Http classes, one that wraps
HttpClient and one that wraps HttpRequest. Those Http classes expose a
unified interface.

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. The fact
that you can "extend" this attempted "abstraction" (which is your main
argument) doesn't make it any better: whoever uses extended class, will
need same "isBrowser" logic.

BTW, I'm not sure your proposal allows exposing specific methods. If it
doesn't, it's a major issue: what is the point to be able to "extend"
something that is no good right from the start? Or maybe you have some
magic way of dealing with the problem, but you didn't explain it yet?
--
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+***@dartlang.org.
Alex Tatumizer
2015-09-02 18:59:12 UTC
Permalink
Interestingly, the argument I'm trying to make was already submitted as an
issue: https://github.com/munificent/dep-external-libraries/issues/1

And it's the SAME argument.

Now, the whole story looks like this: somebody asked to support
configuration-specific imports. Very modest request. 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!!!"

Extend what?

Sending http request from browser is not the same as sending it between
servers. It's not the issue of different names of functions - it's
different set of features and different workflow.
--
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+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2015-09-02 19:31:34 UTC
Permalink
Post by Alex Tatumizer
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?
Yes, it will expose its own platform-independent set of methods. For the
http package, here is the Client class's API
<http://www.dartdocs.org/documentation/http/0.11.3+2/index.html#http/http.Client>.
Most of the API is supported on both platforms. In some cases, you might
have a method that throws UnsupportedError on some configuration.
Post by Alex Tatumizer
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.
Sure, real differences between platforms exist. When you can, you expose
interfaces for the intersections of their features. When you can't, you
either don't support the feature, or only support it on some platforms.
Post by Alex Tatumizer
Interestingly, the argument I'm trying to make was already submitted as
an issue: https://github.com/munificent/dep-external-libraries/issues/1
Yes. Some of what Natalie's asking for—configuration-specific static
analysis—we don't intend to support and almost none of the proposals can
handle.

I believe most of what she wants can be accomplished, though it sometimes
requires an extra import if you want to statically see that you are calling
something configuration-specific.
Post by Alex Tatumizer
And it's the SAME argument.
Now, the whole story looks like this: somebody asked to support
configuration-specific imports. Very modest request.
I will note that that "someone" *was me*. Natalie and I were, as far as I
know, the first people to bring up configuration-specific code in Dart,
well before 1.0. Possibly even before the public release but my memory
fades.

Instead, your proposal deals with a problem: to support
Post by Alex Tatumizer
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!!!"
The original issue has always been how to write code that encapsulates the
use of a "dart:" library that isn't available on all platforms in such a
way that the code can be run on a platform where that library isn't
available. There are a lot of different mechanisms to accomplish that.
Making the import of that "dart:" library itself optional is one. There
isn't a fully-written proposal for "optional imports" like this, but it's
been discussed.

The feeling is that its likely too limited. Even if it isn't, it doesn't
seem able to grow as well as the "interface library" proposal that's the
current front-runner. Future-compatibility matters—we don't want to be
stuck with a feature that's a dead end when new needs appear.

Cheers,

- bob
--
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+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2015-08-31 16:28:21 UTC
Permalink
Post by tatumizer-v0.2
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).
Right. There seems to be a pretty linear graph where one axis is
expressiveness and the other is complexity. We have proposals for very
simple solutions that can't express some interesting use cases, and we have
complex proposals that can express a lot. There isn't any clear hump in the
graph where one proposal has an obviously better power/weight ratio.

Personally, I think Florian's "interface libraries" proposal does stand out
in terms of what it buys you versus what it costs.
Post by tatumizer-v0.2
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?
For any configuration problem that you can solve *at runtime*, Dart already
has you well covered. We have imperative code, control flow, interfaces,
libraries etc. All sorts of fun features for encapsulation, dynamism, and
choosing 1 of N behaviors.

The problem is that *imports* are not imperative. You can't
*dynamically* control
an import, and we have some magical imports ("dart:io", "dart:html", etc.)
that *prevent your program from even loading, much less running.*

All of these proposals exist to work around that last problem. Because you
can't even start main(), you don't have access to all of the existing
language features for dynamism and selecting different code paths. That
means we have to add some new language feature to work around those imports.

Cheers!

- bob
--
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+***@dartlang.org.
'Lasse R.H. Nielsen' via Dart Misc
2015-08-28 07:54:12 UTC
Permalink
Post by tatumizer-v0.2
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
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 by
if (imported(foo)) { // "imported" is an intrinsic function
That's unnecessary (and "being guarded by" is likely to be a Turing
complete problem to decide exactly, so we'll have some approximation only).
We already have a way to talk about a library that may or may not be loaded
- it's a deferred library. In this case it'll just not have a loadLibrary
function because it's always loaded from the start or never loaded at all.
The restrictions on using a deferred library are those that are necessary
to ensure you don't depend on something that isn't there (don't use the
types as types or constants as constants), so we'll likely need the same
restrictions here anyway.
Post by tatumizer-v0.2
// use foo.anything
}
...
if (imported(bar)) {
// use bar.anything
}
Unguarded access is an error.
Too complex. Just say that using the prefix when it isn't loaded, is an
error, like deferred libraries.
You can do the same test as the import if you want to guard access.
Post by tatumizer-v0.2
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.
It has been considered. but isn't universally loved.
The arguments against it is that:
- the code for both versions have to go in the same library, so there is no
platform separation.
- The libraries will be treated as deferred loaded (that's one choice, but
it keeps things simple). That means you can't extend the types of the
library, nor can you use them as type assertions.

I personally like the idea because it's simple and uses only existing
concepts. However, the existing concept it depends on is deferred loading
which I think is an ugly wart on the language that should die a thousand
deaths, so ... maybe it's not that good an idea anyway.

/L
--
Lasse R.H. Nielsen - ***@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
--
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+***@dartlang.org.
Loading...