John, Which callback mechanism feels more Darty to you?
Would also be nice if there was some language feature that objects could opt into that would make properties automatically observable.
I'd like to propose we try to reduce the number of APIs users need to learn and create shared abstractions where appropriate. For example:Unify WebSocketHandler's onOpen and ServerSocket's onConnection to same nameUnify HttpClient and XMLHttpRequest into common interfaceUnify HTML5 FileSystem API and File/Directory from dart:io into common interfaceUnify Timer and window.setTimeout into common interface
Also, we should unify how we assign callback handlers. For example:dart:html is element.on.click.add((e) => ...);dart:io is webSocket.onOpen = (conn) => ...;dart:core is new Timer(1000, (timer) => ...) (then is fine, simply pointing out the callback is passed as parameter)
Based on the feedback we're getting from the hackathons and mailing list, it's clear we're building two worlds that are different and thus more confusing than needs be. We have server libs and client libs with similar intents, but very different APIs.
Unify WebSocketHandler's onOpen and ServerSocket's onConnection to same name
Unify HttpClient and XMLHttpRequest into common interface
Unify HTML5 FileSystem API and File/Directory from dart:io into common interfaceUnify Timer and window.setTimeout into common interface
Also, we should unify how we assign callback handlers. For example:
dart:html is element.on.click.add((e) => ...);
dart:io is webSocket.onOpen = (conn) => ...;
dart:core is new Timer(1000, (timer) => ...) (then is fine, simply pointing out the callback is passed as parameter)
dart:html is element.on.click.add((e) => ...);* namespacing of events into an "on" getter
* overkill for objects with smaller number of events
* more difficult to find which events are available for a given interface
* multi cast (multiple handlers can be added)* protects against clobbering of existing handlers
dart:io is webSocket.onOpen = (conn) => ...;* single cast (only one handler can be added)
* does not protect against clobbering of existing handlers
* setter without getter is surprising to users, as noted by the Dart style guide
dart:core is new Timer(1000, (timer) => ...) (then is fine, simply pointing out the callback is passed as parameter)* single cast, and cannot reassign the single handlerThe above all deal with async events which may occur any number of times. There are also async "operations" which are events guaranteed to occur exactly once, or fail with an exception:The most common approaches outside of Dart are:* pass a callback and an "errback" to the operation itself
* use Futures (or similar).* multi cast
* operation can provide exact same parameter signature (don't need to think about parameter ordering of callback, errback, future which may be added in the future) as if it were a sync operation, merely changing the return type from Foo to Future<Foo>
* can take advantage of an "await" keyword or similar
* allows composition of arbitrary async operations
dart:io async operations generally take a callback, but not an errback, which means error handling cannot be localized for individual operations or even for operation types.
Futures are provided in dart:core, but are rarely used by the core libraries, which is a clear violation of the dogfooding principle :) .
I would suggest the following approach:
* on<EventName> naming convention everywhere, i.e. no "on" getters* create a multi cast Event interface (similar to dart:html's existing EventListenerList), and use it for all events* For async operations use Future everywhere* Have Future extend Event so callback registration mechanism is consistent regardless of whether the event occurs multiple times, which for example would allow using the "await" keyword for arbitrary events.