munificent / dep-external-libraries

Proposal for handling platform-specific code in Dart

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

library extension feature?

jmesserly opened this issue · comments

I was playing around with this, but slicing it a little differently:

  • library extensions: the idea that we "extend" a library
  • some way of configuring which library extension to load in place of an existing one

Here's a sketch of how it would look for package:html:

Key concepts:

  • external is like abstract, but for libraries
  • @patch is an annotation like @override. It is optional but provides documentation/checking.
  • you can give a prefix for the library being extended, this is like super
  • in this sketch, both libraries and each of their types (the "base" and "impl") would show up in reflection.

I think the current proposal "merges" the libraries and types at runtime. That's probably a simpler/better approach, this is just illustrating an alternative.

package:html/html.dart:

// We'd like to provide different implementations for a library
// (e.g. server ane browser), while preserving the same API structure.
// The proposal is to handle this similar to "patch" files already used for
// this in the core libraries. It accomplishes this by using "library extensions".

// external is like "abstract", but for the whole library
external library dart.html; // html.dart

external abstract class Node {
  external Node parentNode, firstChild, lastChild, nextNode, previousNode;

  Element get parent => parentNode is Element ? parentNode : null;
  List<Node> childNodes => new _ChildNodeList(this);
}

external abstract class Element extends Node {
  external factory Element.html(String html);
  external factory Element.tag(String tag);

  external String get localName;
}

package:html/src/standalone_html.dart:

// declares a library extension, and gives a name to the base library.
library dart.html_standalone extends 'package:html/html.dart' as base;

// @patch is like @override but for the library. Optional. Runtime will ignore.
@patch
class Node extends base.Node {
  Node();
}

// Extend base.Element, which then extends our overridden "Node".
// Alternatively `extends Node`, and have the relationship to `base.Element` be implied,
// If we "merge" libraries, this becomes a non-issue: `extends Node` wouldn't even need to
// be repeated.
@patch
class Element extends base.Element {
  @patch
  factory Element.html(String html) {
    // ... parse HTML stand such ...
  }

  Element(this.localName);

  final String localName;
  //...
}

package:html/src/browser_html.dart

library dart.html_browser extends 'package:html/html.dart' as base;

// The browser's view of HTML libs...

user code:

// Now, we just need a way to indicate to the runtime which extension to use, so
// user can write:
import 'package:html/html.dart'; // loads the appropriate extension.
// This can be accomplished through the package spec:
// dart --package-spec

Anyway, not sure we should slice it this way, but maybe it's another perspective for discussion.

Regardless, would it be worth a pull request for the HTML example? (but modified to match the existing proposal's syntax/semantics)

+1 to the concept of declaring a library abstract (via external) and declaring another library to extend it!

I'm not sure that using the package spec for platform-specific library selection can work. Nobody would want to build the package spec manually. And to build it automatically, the base libraries would need to contain the information about which sub-libraries to use on which platforms. And then the code would need to be analyzed before it is run in order to build the package spec.

Unless platform specific libraries were kept in a strict conventional location with respect to the base library, in which case the package spec could be deduced from the file system.