Notes from January 14 language meeting

453 views
Skip to first unread message

Bob Nystrom

unread,
Jan 16, 2013, 8:45:26 PM1/16/13
to General Dart Discussion
This week's meeting was actually a detailed discussion, which is great! But it makes it much harder for me to take notes. Apologies for anything I missed or got wrong.

Configuration

Lars: The top item to discuss is configuration. Gilad has a library object proposal. There's a few things we really want:

1. Want to make sure we can select platform-dependent implementation code. The system should be able to handle code that works different on client and server.
2. A way to to pass in constant values that can be compiled into the snapshot or execution.

I looked at the proposal, and it's hard to see how it solves those problems.

Gilad: It's hard to avoid ending up creating what java did for DI. The proposal is for "business class" not "first class" libraries since they have to be constants. From the VM's perspective, "compile time" is very late. dart2js can handle this ahead of time. It's actually harder in the VM.

Lars: Let's open discussion, and see what solves what.

Gilad: An alternative is to take it out of language and have some sort of config language.

Lars: Kasper's alternate proposal is that "import" could have a compile time "where" clause which is like a conditional.

Gilad: It's specifies bindings?

Kasper: It's a predicate.
Lars: If where clause is positive, it does the import, otherwise it does not.

Gilad: It's certainly simpler, but it doesn't solve generalized config problem.

Lars: Can you come up with an example?

Me: Say you're using some package "foo" that in turn uses a logging package. You want to swap in your awesome logging package even though "foo" knows nothing about it and wasn't designed with that abstraction in mind.

[Some discussion here about DI not being the most immediate problem but also not wanting to paint ourselves into a corner by putting it off...]

Lars: We want to make it easy to do the things that users do most.

Gilad: I want some use cases.

Lars: One problem is that you cannot write package that has both client and server code embedded in it.

[Some more discussion here...]

Lars: The two top issues:

1. Platform-dependent parts.
2. Can set constants from outside.

Gilad's proposal would address 1 like:

    library d({
      x: LIbrary.platform.name ='dartjs'
          ? Library.fromURI('dart:html')
          : Library.fromURI('dart:io')
    })

Brian: That gets pretty complicated quickly if you have more complicated conditions.

[Long discussion about the scope of these constant expressions...]

Ivan: can the import URL itself be one of these external constants?

Kasper: We want to avoid that because it makes it really hard for the Editor to give you a good user experience.

Gilad: In my proposal, default values are there to help here. You always have one library to use for analysis.

Kasper: One issue is that ?: isn't a const expression.

Kasper: We have things that only work with type names like "is" checks, etc. These could be expanded to work with any constant expression.

[Missed some stuff here...]

Ivan: My main concern with Gilad's proposal is that constant definition can appear anywhere and imports can refer to them.

Gilad: Right, think we need some kind of delimited region for constants.

Lars: So the two cases we're worrying about are:
1. Simple cases where you know set of options you're selecting from.
2. Open-ended dependency injection.

Kasper: I worry about making the system harder to reason about.

Brief mention of stuff for next week's meeting

Mixing optional positional and named parameters: Kasper says only one place in dart2js cares about this, so it may be doable.

Ignoring extra function arguments in closure calls: Lars says they don't want this.

Forwarding optional arguments: Lars has a syntax proposal.

Cheers!

- bob

Ladislav Thon

unread,
Jan 17, 2013, 12:45:35 AM1/17/13
to General Dart Discussion

(So this is the post where I finally reveal myself as totally incompetent.)

> 1. Want to make sure we can select platform-dependent implementation code. The system should be able to handle code that works different on client and server.
> 2. A way to to pass in constant values that can be compiled into the snapshot or execution.

From these two points, the first is easily solvable by the predicated imports mechanism that Kasper suggests (and I already suggested it too, and even described in dartbug.com/6943).

And the second one... what is it really supposed to do? I can't see how it can solve the "general configuration" problem... and frankly, I don't really understand what this problem really looks like.

> Gilad: It's certainly simpler, but it doesn't solve generalized config problem.

But what that problem is?

> Me: Say you're using some package "foo" that in turn uses a logging package. You want to swap in your awesome logging package even though "foo" knows nothing about it and wasn't designed with that abstraction in mind.

I firmly believe that putting dependency injection into the language is ultimately a good thing, but I'm not sure that Dart is a good fit for that.

Don't we have some other example of the "general configuration" problem that isn't solved by predicated imports but doesn't immediately lead to dependency injection? Or is this "general configuration problem" already understood to be equal to DI?

> Gilad: I want some use cases.

Me too.

> Lars: One problem is that you cannot write package that has both client and server code embedded in it.

Predicated imports, anyone?

> Lars: The two top issues:
>
> 1. Platform-dependent parts.
> 2. Can set constants from outside.

Been there...

> Gilad's proposal would address 1 like:
>
>     library d({
>       x: LIbrary.platform.name ='dartjs'
>           ? Library.fromURI('dart:html')
>           : Library.fromURI('dart:io')
>     })

I can't see how this solves more problems than the predicated imports stuff... except that it has much more complicated syntax. That is good for consultants, but noone else.

> Kasper: One issue is that ?: isn't a const expression.

Gilad says it's perfectly doable: dartbug.com/5558

> Lars: So the two cases we're worrying about are:
> 1. Simple cases where you know set of options you're selecting from.
> 2. Open-ended dependency injection.

Oh! That's probably it.

So, if we use the logging example, then point 1 is about enabling/disabling logging or setting a minimal log level, while point 2 is about choosing logger implementation (potentially external, so thatit doesn't degenerate to 1). Is this any more complicated? If not, do we really need this inside the language? This stuff was solved on the application level like a gazillion times. Is this about helping dart2js to produce smaller output by doing some decisions ahead of time?

LT

Chris Buckett

unread,
Jan 17, 2013, 4:23:09 AM1/17/13
to General Dart Discussion
Good to see these areas being discussed :)

From my naive view (and I'm ready to hear args why this wouldn't work), why not:

2. Can set constants from outside.
When defining a constant, you could have something like:
  const String s = "myconstant" external; // ie, look for a myconstant field in the external file

Then read constants from a file, that is deployed along with the dart code.  

Dart code could import it, eg: my entry point app file could declare:
  import "constants:myfile.yaml"; 
which would / could supply all required constants for all imports that I use.  It would be an error if there are external constants defined that aren't supplied in an external file.

When running via dart vm, the external file would by loaded along with the .dart and .html files.
When running via dart2js, I guess you could leave it as an option whether to replace the external constants into the output javascript, or have the js dynamically load the values from an externally hosted file (which would also need to be deployed along with the .dart.js and .html files).

 
1. Platform-dependent parts.
Could this be solved in a similar way to lazy loading libraries? - in other words, if I want to load a library at some point then what's to stop me from switching that library to another implementation?

Cheers,
Chris.


Alex Tatumizer

unread,
Jan 17, 2013, 9:34:28 AM1/17/13
to mi...@dartlang.org
> But what that problem is?

I have the same question every time :)



Sean Eagan

unread,
Jan 17, 2013, 9:56:00 AM1/17/13
to General Dart Discussion

For the most immediate use case, platform dependent imports/exports, you want to choose between different libraries to import/export, not selectively import a single library.  So I think the where clause could get unwieldy and error prone:

import 'package:foo/browser.dart' as foo show bar, baz, bang where platform == 'browser';
import 'package:foo/standalone.dart' as foo show bar, baz, bang where platform == 'standalone';

I think being able to use any constant String expression as the import/export URI would be better:

import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang;

I don't think string interpolation is currently considered constant, but it should be when it is guaranteed to yield a String, since there is no need to execute an arbitrary toString() in that case.  Obviously ternary expressions should be able to be constant too.

Presumably this "platform" constant could be defined in a library e.g. 'dart:platform' which one could import.  The editor/analyzer could expose a setting for which platform to analyze with.

This could solve the general problem of how to use constants to control imports/exports.  The general problem of how to externally define constants (which could be used even beyond the scope of imports/exports) could be saved for later.

Ladislav Thon

unread,
Jan 17, 2013, 10:05:15 AM1/17/13
to mi...@dartlang.org
For the most immediate use case, platform dependent imports/exports, you want to choose between different libraries to import/export, not selectively import a single library.

Conditional imports, yes.
 
 So I think the where clause could get unwieldy and error prone:

import 'package:foo/browser.dart' as foo show bar, baz, bang where platform == 'browser';
import 'package:foo/standalone.dart' as foo show bar, baz, bang where platform == 'standalone';

That's what I'm suggesting in http://dartbug.com/6943.
 
I think being able to use any constant String expression as the import/export URI would be better:

import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang;

That isn't bad, actually. I would have to think about it some more.
 
I don't think string interpolation is currently considered constant, but it should be when it is guaranteed to yield a String, since there is no need to execute an arbitrary toString() in that case.  Obviously ternary expressions should be able to be constant too.

A restricted form of string interpolation actually is constant (the restriction is that the interpolated expression must itself be constant). The library directives, however, do not permit interpolations at all.
 
Presumably this "platform" constant could be defined in a library e.g. 'dart:platform' which one could import.  The editor/analyzer could expose a setting for which platform to analyze with.

This could solve the general problem of how to use constants to control imports/exports.  The general problem of how to externally define constants (which could be used even beyond the scope of imports/exports) could be saved for later.

Yeah, platform-dependent imports are the most important thing and it's the only thing I heard users calling for. I also think that the we can postpone the "general configuration" problem.

LT

Alex Tatumizer

unread,
Jan 17, 2013, 10:21:26 AM1/17/13
to mi...@dartlang.org
"Every problem in programming is solved by another level of indirection"
If this is true, then

import '@{package:foo}' as foo show bar, baz, bang

where all expressions like "@package:foo" are defined in some dart file just as a map.
So every module has implicit dependency on some DartConfig (normal dart class),
which can be called as DartConfig.getMap() - it will return the all mappings, like:
package.foo -> package:foo/standalone.dart etc.

I think it's a simpler solution, no? And, unlike config-file-based dependency injection, where settings come from static file,
here we have DYNAMIC program that computes the settings.

Does it make sense? 


Sean Eagan

unread,
Jan 17, 2013, 11:04:23 AM1/17/13
to General Dart Discussion
On Thu, Jan 17, 2013 at 8:56 AM, Sean Eagan <seane...@gmail.com> wrote:

import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang;


and in most cases, to keep the platform specific library names consistent, you would probably just do:

import 'package:foo/$platform.dart' as foo show bar, baz, bang;

and as Ladislav pointed out in http://dartbug.com/6943, it would probably more often be done with exports than imports:

library foo;

export '$platform.dart';

Cheers,
Sean Eagan

Alex Tatumizer

unread,
Jan 17, 2013, 11:55:52 AM1/17/13
to mi...@dartlang.org
> import 'package:foo/$platform.dart' as foo show bar, baz, bang
But why stop here?
Can't we just introduce special type of interpolation (similar to ${..}) - with @ instead of $, and in every place (including import) where we have "...@{foo}", we call DartConfig.getMapping("foo").

I don't know what "general configuration problem" is, but, just as in case with monad, maybe solution can define a problem :)

Sean Eagan

unread,
Jan 17, 2013, 12:02:44 PM1/17/13
to General Dart Discussion

And actually, using "part" would probably be better than "export" because then the parts can declare their "part of" status, and it would prevent the platform specific files from ever being accidentally imported:

library foo;

import 'dart:platform' as platform;

part '${platform.name}.dart';

Cheers,
Sean Eagan

Bob Nystrom

unread,
Jan 17, 2013, 12:45:08 PM1/17/13
to General Dart Discussion
So much to reply to! I'll try to answer what I can all in one lump:

> 1. Want to make sure we can select platform-dependent implementation code. The system should be able to handle code that works different on client and server.
> 2. A way to to pass in constant values that can be compiled into the snapshot or execution.
From these two points, the first is easily solvable by the predicated imports mechanism that Kasper suggests (and I already suggested it too, and even described in dartbug.com/6943).
And the second one... what is it really supposed to do? I can't see how it can solve the "general configuration" problem... and frankly, I don't really understand what this problem really looks like.

Dependencies and modularity are deeply close to Gilad's heart and I think by "general configuration" he means that any module can have all of its dependencies controlled by the module that uses it. This is probably a good start to see where he's coming from: http://gbracha.blogspot.com/2009/06/ban-on-imports.html

I believe he's also worried that not trying to solve dependency management generally will mean that the Dart community will eventually recapitulate OSGI, which would seem to be a kind of moral crime according to some people on the team.

So, if we use the logging example, then point 1 is about enabling/disabling logging or setting a minimal log level, while point 2 is about choosing logger implementation (potentially external, so thatit doesn't degenerate to 1). Is this any more complicated? If not, do we really need this inside the language? This stuff was solved on the application level like a gazillion times.

I think that's the right idea. I just think Gilad believes solving it at the application level will be inelegant, but I don't speak for him so don't hold me to that.

Dart code could import it, eg: my entry point app file could declare:
  import "constants:myfile.yaml"; 
which would / could supply all required constants for all imports that I use.  It would be an error if there are external constants defined that aren't supplied in an external file.

That does raise the question of how do you avoid baking a concrete file path into your code. I think this trades one abstraction for another and I'd personally prefer to not use the file system as my abstraction boundary. :)

I think being able to use any constant String expression as the import/export URI would be better: 
import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang; 
I don't think string interpolation is currently considered constant, but it should be when it is guaranteed to yield a String, since there is no need to execute an arbitrary toString() in that case.  Obviously ternary expressions should be able to be constant too.

This was discussed, more or less. The problem here is that now the editor has to be able to evaluate those constant expressions in order to figure out what libraries to use to analyze your code. If the import path is a simple literal but then whether or not it gets processed is conditional, then the Editor can easily find all of the libraries you may import and it can let you see how the different conditions would affect your code.

Cheers!

- bob

Sean Eagan

unread,
Jan 17, 2013, 5:16:48 PM1/17/13
to General Dart Discussion
On Thu, Jan 17, 2013 at 11:45 AM, Bob Nystrom <rnys...@google.com> wrote:
I think being able to use any constant String expression as the import/export URI would be better: 

import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang; 

I don't think string interpolation is currently considered constant, but it should be when it is guaranteed to yield a String, since there is no need to execute an arbitrary toString() in that case.  Obviously ternary expressions should be able to be constant too.

This was discussed, more or less. The problem here is that now the editor has to be able to evaluate those constant expressions in order to figure out what libraries to use to analyze your code. If the import path is a simple literal but then whether or not it gets processed is conditional, then the Editor can easily find all of the libraries you may import and it can let you see how the different conditions would affect your code.

But how would the editor/analyzer use this finite set of potential dependency graphs to help the user?  I suppose it could:

* warn if an expression is invalid in any (every?) possible scenario
* union together all possible types for expressions
* only treat an expression as const if it's const in all possible scenarios
* warn if a library exposes a different public API for different dependency graphs

But that seems pretty complicated, and unscalable.  I think only allowing private members in conditionally "part"ed files would help?  Then the importing library always has a consistent API to use, regardless of the configuration.  And dartdoc would almost need this property in order to be able to document the library.

If a branching approach is taken, maybe instead of a where clause, just allow if/else/switch at the top-level:

library http;

import 'dart:platform' as platform;

switch(platform.name) {
  case 'client': 
    import 'dart:html';
    part 'src/client.dart';
  case 'standalone': 
    import 'dart:io';
    part 'src/standalone.dart';
}

/// Makes an http GET request.
Response get(String uri) => _get(uri);

compare the bold section above to a where clause version:

import 'dart:html' where platform.name == 'client';
part 'src/client.dart' where platform.name == 'client';

import 'dart:io' where platform.name == 'standalone';
part 'src/standalone.dart' where platform.name == 'standalone';

Cheers,
Sean Eagan
 

Sean Eagan

unread,
Jan 17, 2013, 5:33:11 PM1/17/13
to General Dart Discussion
Gilad: It's certainly simpler, but it doesn't solve generalized config problem.

Lars: Can you come up with an example?

Me: Say you're using some package "foo" that in turn uses a logging package. You want to swap in your awesome logging package even though "foo" knows nothing about it and wasn't designed with that abstraction in mind.

I wonder if that could be handled by pub.  In your pubspec.yaml you could have aliased dependencies:

  logging: 
    alias : myLogging
  myLogging : any

Cheers,
Sean Eagan

Istvan Soos

unread,
Jan 17, 2013, 5:47:24 PM1/17/13
to General Dart Discussion
>> So, if we use the logging example, then point 1 is about enabling/disabling logging or setting a minimal log level, while point 2 is about choosing logger implementation (potentially external, so thatit doesn't degenerate to 1). Is this any more complicated? If not, do we really need this inside the language? This stuff was solved on the application level like a gazillion times.
>
> I think that's the right idea. I just think Gilad believes solving it at the application level will be inelegant, but I don't speak for him so don't hold me to that.

my 2c, in the hope it might induce a few more ideas in the field:

I think it would be beneficial if we would make a visible difference
between traditional linking ("switching the log library" and "having
loaded two log libraries") from runtime environment behavior ("turning
off logging").

Mixing configuration-like items with code works as long as you are
able to keep _everything_ about your program in your head. Eventually,
the program needs to run in a specific environment, in a specific
configuration. When (and not if!) you encounter problems, you will
need to reconstruct all these configuration bits and look for
something that could be broken. Speaking from recent experiences, it
is hard to understand issues if your "configuration" is hidden/obscure
behind the semi-random mixture of several layers of externally-linked
files, code generators, abstractions and automagic injections.

Whatever language/library construct you choose, I thing the following
items would improve the actual practices:
- have a clear and well-defined description of what is the
configuration of the software and what is it used for,
- provide a way to use a _single_ place to store it (it might have the
feature to split, mix and share),
- an easily debuggable way to re-construct each different permutation
of the configurations.

Regards,
Istvan

Istvan Soos

unread,
Jan 17, 2013, 5:58:28 PM1/17/13
to General Dart Discussion
On Thu, Jan 17, 2013 at 9:45 AM, Bob Nystrom <rnys...@google.com> wrote:
>> Dart code could import it, eg: my entry point app file could declare:
>> import "constants:myfile.yaml";
>> which would / could supply all required constants for all imports that I
>> use. It would be an error if there are external constants defined that
>> aren't supplied in an external file.
>
> That does raise the question of how do you avoid baking a concrete file path
> into your code. I think this trades one abstraction for another and I'd
> personally prefer to not use the file system as my abstraction boundary. :)

+1

I'd prefer the following fallback-mechanism, more as a separate
configuration-reader library than a language construct:

Preparation:
- if there is an environment variable that specifies one or more
config file, load it,
- if there is a command-line argument that specifies one or more
config file, load it,
- merge these into a single flat map and in each case, the last
specified value wins,

Evaluation:
- if there is a command-line argument, use it (e.g. --logging=1)
- if there is a loaded config value (from the above config files), use it,
- if there is an environment variable present, use it (e.g. export LOGGING=1)
- if there is a reasonable default value, use it (developer's choice)
- otherwise throw an exception, as we are missing a critical configuration value

Regards,
Istvan

Don Olmstead

unread,
Jan 17, 2013, 6:12:08 PM1/17/13
to mi...@dartlang.org
Clearly what needs to be added is a full on preprocessor like C/C++ ;)



--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart



Alex Tatumizer

unread,
Jan 17, 2013, 11:34:29 PM1/17/13
to mi...@dartlang.org
>> import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang; 

... you have 100 files, and you copy this nice construct into each of them?

Multiply by the number of "imports".

Most likely, there's a naming convention in your project: all browser modules are in "browser" directory, standalone are in "standalone", but you still have to write.
 import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang; 
 import 'package:bar/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as ... ; 

etc.

And if you need third alternative (e.g., for mock objects or whatnot), you go and modify 100 files.

No place to implement naming conventions, or any centralized logic. 

To be fair, dependency injection a-la spring framework is a nightmare, too (on top of being ugly).

What's wrong with keeping it simple? Every class can get whatever it needs from an object that knows what is what:
class Foo {
    Logger logger=Config.getLogger("Foo"); // Config is a normal class.
}

Config class is user-defined (but its name and location are fixed). It can be completely data-driven (reading some Yaml), partially data-driven, or completely programmatic.
It can supply same logger for every class, or be selective (for Foo, there's something special). If I'm not mistaken, this is called "mediator" pattern. . 

This solves all configuration problems, except one: import statement. Import statement is a showstopper: it cannot be coded like that (on top of the reasons cited in Gilad's article). The best it can do is use Config-supplied variables for substitution 






Ladislav Thon

unread,
Jan 18, 2013, 2:49:11 AM1/18/13
to mi...@dartlang.org
>> import 'package:foo/${platform == 'browser' ? 'browser' : 'standalone'}.dart' as foo show bar, baz, bang; 

... you have 100 files, and you copy this nice construct into each of them?

Put it into export instead of import.
 
To be fair, dependency injection a-la spring framework is a nightmare, too (on top of being ugly).

You mean like Spring in the old days of 1.2 with the huge amount of XML? That's years ago. Look at modern Spring or Guice.
 
What's wrong with keeping it simple? Every class can get whatever it needs from an object that knows what is what:
class Foo {
    Logger logger=Config.getLogger("Foo"); // Config is a normal class.
}

Very limited. Again, look at Guice, they have done some impressive things in this regard.

Also, we have factory constructors. Can't we use them somehow in this?

LT

Ladislav Thon

unread,
Jan 18, 2013, 3:48:53 AM1/18/13
to mi...@dartlang.org
Dependencies and modularity are deeply close to Gilad's heart and I think by "general configuration" he means that any module can have all of its dependencies controlled by the module that uses it. This is probably a good start to see where he's coming from: http://gbracha.blogspot.com/2009/06/ban-on-imports.html

I can confess that I have a hard time understanding that, possibly because I have been infected by the DI idea :-) Anyway, two concerns come to mind:

1. Dart is conservative by design, yet it brought a decent amount of innovation already. And you did great, it feels all natural! Hope that this will feel natural as well, because it has a disrupting potential.

2. The library concept in Dart looks like a pretty traditional design. (Or maybe not? It permits mutually dependent libraries) Is it easily extended, or will it change dramatically?
 
I believe he's also worried that not trying to solve dependency management generally will mean that the Dart community will eventually recapitulate OSGI, which would seem to be a kind of moral crime according to some people on the team.

Myself included :-) But then, there are some not-so-heavy implementations of the DI concept. Heck, one of them is a Google project :-)
 
So, if we use the logging example, then point 1 is about enabling/disabling logging or setting a minimal log level, while point 2 is about choosing logger implementation (potentially external, so thatit doesn't degenerate to 1). Is this any more complicated? If not, do we really need this inside the language? This stuff was solved on the application level like a gazillion times.

I think that's the right idea. I just think Gilad believes solving it at the application level will be inelegant, but I don't speak for him so don't hold me to that.

Fair enough. I have always thought that putting DI into the language is a great idea, but it looks like Gilad thinks that DI is a degenerate and the solution should be much better. I'm always for better, even if I can't imagine how it will look like :-) Eager to see more of this stuff.

LT

Alex Tatumizer

unread,
Jan 18, 2013, 8:33:37 AM1/18/13
to mi...@dartlang.org
@Ladislav: 
>> Very limited...

Any example of what exactly is limited?

Forget for a second the problem of configuration. No configuration at all.
Suppose you have a bunch of objects, and they want to interconnect - just by finding each other.
You mean, having Turin-complete programming language is not enough to do it? You need a framework?

I'd like to understand the reasons... Formulating a problem we are trying to solve won't be a bad idea either.

Ladislav Thon

unread,
Jan 18, 2013, 8:39:20 AM1/18/13
to mi...@dartlang.org
Suppose you have a bunch of objects, and they want to interconnect - just by finding each other.

And for testing, you want to wire them differently than for production. And for production, you want them wired differently in different deployments. And...

Yeah, you can do everything with a simple class. But, and that's the very point of Dart, you usually want to use more structured approach.

LT

Chris Buckett

unread,
Jan 18, 2013, 8:45:56 AM1/18/13
to General Dart Discussion

And for testing, you want to wire them differently than for production. And for production, you want them wired differently in different deployments. And...

+1 for that use case.

Alex Tatumizer

unread,
Jan 18, 2013, 12:08:47 PM1/18/13
to mi...@dartlang.org
> And for testing, you want to wire them differently than for production. And for production, you want them wired differently in different deployments

That was exactly my point.
Config class KNOWS everything. It may take environment variable, say, RUN_OPTION (=test, production, server, client, etc), or read this option from yaml, or retrieve it from somebody's email, and, by using regular programming logic, and arrange things accordingly.

I don't see any way to avoid writing program to implement some logic for that, even in simplest cases (e.g. for naming conventions). Declarative syntax is always lacking, and over the time tends to grow into another variant of Turing machine (very crippled one). Every xml now has a handmade "if", variables, loops etc.

I'm not proposing anything new. It's just mediator pattern, specifically designed for this purpose. It's not that Config cannot read metadata, yaml files, env vars, etc. It can (and will) do that, but on top of it - there's always procedural logic.

With this pattern, you have normal control flow, you can understand it, debug it, etc.

I'd like to see example where DI clearly wins - does something that superior to mediator approach (easier to understand, debug etc.)


Ladislav Thon

unread,
Jan 20, 2013, 4:13:53 AM1/20/13
to mi...@dartlang.org

Config class KNOWS everything.

Then it isn't a mediator, it is a god object :-)

LT

Alex Tatumizer

unread,
Jan 20, 2013, 8:58:38 AM1/20/13
to mi...@dartlang.org
> Then it isn't a mediator, it is a god object :-)
+1

Yeah, user-defined god object
(I really like you conjecture mediator= god object. Maybe you are on to something :)

Alex Tatumizer

unread,
Jan 20, 2013, 12:00:25 PM1/20/13
to mi...@dartlang.org

Let's consider example from guice tutorial to find out what is good about DI,
and why same goodness cannot be achieved by simpler means.

public class BillingService { 
  private final TransactionProcessor processor;

  @Inject
  public BillingService(TransactionProcessor processor) {
    this.processor = processor;
  }
}

and somewhere in Config provider, we have:

bind(TransactionProcessor.class).to(RealTransactionProcessor.class);

Juice provides an entire zoo of different types of bindings: we can configure 
specific binding for given class, or for a declarations annotated in a certain way, etc.

Let's see how we could program BillingService without dependency injection:
public BillingService(Config config) {
  this.processor = (TransactionProcessor)config.resolve(BillingService.class, TransactionProcessor.class);
}

And here we really see the difference!
Our version is ugly.
There are several sources of ugliness
- we need to explicitly pass classes BillingService.class and TransactionProcessor.class, otherwise config will never know what we are talking about. Juice gets this information simply by reflection

- return value of config.resolve is a generic Object, we need to cast it to TransactionProcessor explicitly. Juice uses reflection - no such problem

- we need to pass Config object to constructor explicitly (Juice avoid this: it "knows" what Config to use)

- no way to pass annotations (juice gets them by reflection)

In one word: we have a lot of boilerplate and noise where Juice doesn't. 

However, if you get into the root of the problem, it has nothing to do with configuration per se. Same problem arises in many other settings.
Let's try to define the problem:

PROBLEM: language has to provide access to static context.
 
Suppose we have a built-in pseudo-variable "static context". For brevity, let's call it $ (we can find a better name later). 
From $, we can find out the name of the class, the name of required  interface, the name of the current method - everything compiler knows at the point of invocation. Now we can rewrite it as:

public BillingService(Config config) {
  this.processor = (TransactionProcessor)config.resolve($);
}

Now config has enough informaton to figure out who requests what, because:
$.getCurrentClass() returns BillingService.class
$.getTargetClass() returns TransactionProcessor.class
$.getCurrentMethod() returns Method of constructor
$.getTargetDeclaration (mirror of declaraion of "processor" in dart, no java equivalent)  
etc., etc.

This leaves us with 2 problems: 
1. every caller should pass Config as parameter
2. explicit cast to TransactionProcessor

To address the first problem, "Config" object can become a singleton (I don't know how bad it is) 

private static Config config=Config.getInstance();

public BillingService() {
  this.processor = (TransactionProcessor)config.resolve($);
}

Second problem has no obvious solution in java.
However, in dart the problem doesn't exist, we can write simply
BillingService() {
  this.processor = config.resolve($);  
}

Is it a good idea? What is the downside? I think static context $  might be helpful in other  scenarios, so it's not an ad-hoc feature.

Alex Tatumizer

unread,
Jan 21, 2013, 3:33:32 PM1/21/13
to mi...@dartlang.org
How do you think, is the idea of passing static context into runtime for the purposes of configuration patentable?
Need to consult with my lawyer. Will let you know how it goes.


Alex Tatumizer

unread,
Jan 21, 2013, 4:12:36 PM1/21/13
to mi...@dartlang.org

Major hiccup: I completely forgot, I don't have a lawyer!
Instead, I found this:
http://www.google.com/patents/US8245210
It's pretty recent. But I don't think they generalize it to the same extent (e.g. they don't make it explicitly accessible to user, no mention of configuration etc). Looks similar, but different in scope and application.

Hey, Dart team, do you have a lawyer? I give you the whole idea for free!
Well, not exactly free. Maybe T-shirt? How about that? :)





Ladislav Thon

unread,
Jan 21, 2013, 4:21:55 PM1/21/13
to mi...@dartlang.org
How do you think, is the idea of passing static context into runtime for the purposes of configuration patentable?

I believe Gilad is writing about this exact thing in http://gbracha.blogspot.cz/2009/07/ban-on-imports-continued.html:

the only need for a global namespace is for configuration: linking the pieces of an application together

Of course, slightly different words...

LT

Alex Tatumizer

unread,
Jan 21, 2013, 4:39:46 PM1/21/13
to mi...@dartlang.org

> Of course, slightly different words...
Actually, I tried to read this article before.
Honestly, I was not able to understand it (it refers to many things I don't know). Not sure it's the same idea (or even related). Only Gilad himself can tell.

The thing is: $ is a powerful feature, it's not for configuration only.
E.g. you can say: log($, "Hello World") - it will print a containing class name, method name etc automatically, extracting them from $. Just a simple example, I'm sure there can be many more. Eliminates a lot of unnecessary boilerplate.
I'm not aware of any language having this feature, but who knows... Maybe in lisp it's automatically available, but not in compiled languages AFAIK





Alex Tatumizer

unread,
Jan 22, 2013, 12:19:13 AM1/22/13
to mi...@dartlang.org
Obvious disadvantage of the above syntax is that it forces you to pass $ explicitly, which can quickly become annoying.
It can be easily cured by small dose of sugar: each function marked in some special way would receive $ automatically from caller
e.g. we can use named parameter  (recognized by type: StaticContext)
void log(String message, {StaticContext callerContext}) {...}  
so
log("Hello, World")  
will automatically pass $ as parameter "callerContext" 

NOTE: This feature makes it possible to implement objects that are aware of their names at birth (question I asked earlier), e.g.
var sum1=new Token<int>();

If we declare Token constructor with StaticContext parameter (as above), it will get access to $, from which it extracts declaration of variable sum1..

I'm in the process of figuring out other use cases. Will keep you posted.

 
Reply all
Reply to author
Forward
0 new messages