gbracha / metaclasses Goto Github PK
View Code? Open in Web Editor NEWProposal for metaclasses in Dart
License: Apache License 2.0
Proposal for metaclasses in Dart
License: Apache License 2.0
The example of using metaclasses in a typed way seems broken (or else there's something that I'm missing, and that hence could perhaps use better explanation ;) ).
class B<T extends A.class> {
T GetBySmth() {
return T.new(); // could even be new T() if we went that far
}
}
The constraint is perfectly reasonable, as I understand this. It says that T will be instantiated with a type which is a subtype of A.class: that is, that things of type T will be objects that implement the interface A.class. But T.new() doesn't return a subtype of A.class as I understand it (at least not for non-trivial A - hopefully this example isn't relying on the property that A is trivial?). So the typing of GetBySmth is wrong - it claims to return an instance of a meta class, but actually returns an instance of a class. Compare:
class A {
static int s() => 3;
int d() => 4;
}
// B is parameterized over class types subtyping A.class
// B has a method foo which given a Type object
// implementing the interface of A.class, returns it.
// B also has a method bar which will instantiate T, producing an instance
// of some subtype of A.
class B<T extends A.class> extends A {
B(int x);
T foo(T c) => c;
A bar() => T.new(); // known safe, since T extends A.class
}
// We can instantiate B as follows:
B b = new B<A.class>(0);
// Calling foo on a B returns the Type object for A, which has an s method which
// returns an integer, but does not have a d method.
b.foo(A).s(); // ok
b.foo(A).d(); // bad
// Calling bar on a B returns a new instance of A, which has a d method,
// but not an s method
b.bar().s(); // bad
b.bar().d(); // ok
// Even though B extends A, we cannot instantiate B with B.class, since B.class is not
// a subtype of A.class and hence does not satisfy the bound.
B badB = new B<B.class>(0); // bad
// If we ignore the type error, we can call bar, which tries
// to call the default constructor on B (which has none).
badB.bar(); // bad
// C is parameterized over instance types subtyping A
// C has a method foo which given a Type object implementing the interface of
// T.class, returns it.
// C also has a method bar which will attempt to instantiate T and return an instance
// of a subtype of A. If T has a default constructor,
// then the instantiation will succeed, but if not this will be a dynamic error.
class C<T extends A> extends A {
C(int x);
T.class foo(T.class c) => c;
A bar() => T.new(); // unsafe, may fail at runtime
}
// We instantiate C as follows:
C c = new C<A>(0);
// Calling foo on a C returns the Type object for A, which has an s method which
// returns an integer, but does not have a d method.
c.foo(A).s(); // ok
c.foo(A).d(); // bad
// Calling bar on a C returns a new instance of A, which has a d method,
// but not an s method
c.bar().s(); // bad
c.bar().d(); // ok
// We can instantiate C with anything that extends A
C okC = new C<C>(0); // ok
// But calling bar still blows up, since the call to new is dynamic and C has
// no default constructor.
okC.bar(); // bad
I think the issue with the method bar above is what the original example was trying to get at: given a type parameter T with a class type bound (as in B above), we know the contract associated with the type object for T (specifically in this case, whether or not it has a default constructor), but we don't have any way to get from a class type to the instance type, and we also cannot usefully instantiate T with any subtypes.
Alternatively, given a type parameter T with an instance type bound (as in C above), we know the contract associated with instance of T, and we can instantiate T with useful subtypes of the bound. We can get the type object for T (simply by using it as a type literal), and we can get the class type for T (via T.class) but we do not know anything about the contract implied by T.class, and hence we can only do T.new() (and other static methods) in a type unsafe fashion.
This is presumably the point of the comment at the bottom of the examples section: in order to get the best of both worlds, you need both some way of specifying the additional class constraint on the type parameter, and some way of allowing more than one type to satisfy the constraint.
I'd be really happy to see this proposal implemented in Dart.
Right now I am using build step to generate additional code to create a reasonable API in uix library:
class Button extends Component {
final tag = 'button';
updateView() { updateRoot(vRoot()('Button Text')); }
}
// auto-generated {
createButton() => new Button();
vButton() => vComponent(createButton);
// }
class App extends Component {
updateView() {
if (state == 0) {
updateRoot(vRoot()([
vButton()
]));
} else {
updateRoot(vRoot()([
vButton(),
vButton()
]));
}
}
}
Nodes prefixed with v
are "virtual dom" nodes, they are used to create "declarative-like" api for dom transformations. Each time updateRoot
is called, it will perform diff/patch algorithm to find differences between old and new "virtual dom" nodes and apply real transformations to the actual dom. In the above case, when app goes from state '0' to state '1', new button will be appended and createButton
function will be called to create real dom element and internal state for this new button.
Each frame there could be thousands of virtual nodes created just to find that one button is added that will result in one createButton
call.
With metaclasses it will be possible to remove build step.
class Button extends Component { .. }
class App extends Component {
updateView() {
if (state == 0) {
updateRoot(vRoot()([
vComponent(Button)
]));
} else {
updateRoot(
vRoot()([
vComponent(Button),
vComponent(Button)
])
);
}
}
}
I am aware of generalized tear-off proposal, but vComponent(Button)
is much better than vComponent(new Button#)
:) And if I am not wrong, new Button#
will create a closure each time it is called(or it will be optimized?), and I'd prefer to just pass a reference because of performance reasons.
(I was told to file this feedback here, so here goes:)
Metaclasses are the main pain point I have in dealing with Dart in the context of developing extensible platforms, i.e. platforms where core features get extended post-hoc. In particular I miss:
For example:
abstract class LayoutManager { ... }
void registerLayoutManager(Class<LayoutManager> manager, String name) { ... }
...where registerLayoutManager()
's first argument has to be a subclass of LayoutManager
(or indeed LayoutManager
itself, if it wasn't abstract).
For example:
class Foo {
Foo() { print("constructor"); }
static String get name => 'my-name';
static void test() => print('foo');
}
void main() {
var X = Foo;
Foo f = new X(); // prints "constructor"
print(X.name); // prints "my-name"
}
// assume Foo from above exists
class Bar extends Foo {
@override static String get name => 'bar';
@override void test() { print('bar'); super.test(); }
}
void register(Class<Foo> x) {
print("going to register ${x.name}");
}
void main() {
Foo X = Bar;
X.test(); // prints "bar" then "foo";
register(X); // prints "going to register bar"
register(Foo); // prints "going to register my-name"
}
Virtual static fields are great for storing data about the class, as above. Virtual static methods are really useful too, though, for the same reason that regular static methods are useful: it lets you do logic that applies to all instances, without having to know which exact class you're dealing with. For example, a class could define a method that verifies if some data is valid input to the class; you would then check this before calling the constructor. With metaclasses, you don't know exactly which class you have, but you still need to check the data. See the example below.
The above are all useful, but really virtual constructors are the most important:
abstract class Element {
new(String input);
static String get tagName;
static bool isValid(String input);
// ...
}
class FooElement extends Element {
@override new(String input): super.new(input) { /*...*/ }
@override static String get tagName => 'foo';
@override static bool isValid(String input) => (input == 'true') || (input == 'false');
}
class BarElement extends Element {
// inherits Element's constructor
@override static String get tagName => 'bar';
@override static bool isValid(String input) => (input == 'one') || (input == 'two');
}
Map<String, Class<Element>> registrations = {};
registerElement(Class<Element> e) {
registrations[e.tagName] = e;
}
instantiateElement(String tagName, String input) {
var c = registrations[tagName](input);
if (c.isValid(input))
return new c(input);
return null;
}
void main() {
registerElement(FooElement);
registerElement(BarElement);
// ...
assert(instantiateElement('foo', 'true') is FooElement);
assert(instantiateElement('bar', 'one') is BarElement);
}
This seems like it would provide a way of closurizing a constructor, without needing the extra syntax in that proposal (https://github.com/gbracha/generalizedTearOffs) Instead of MyClass#constructorName you could write MyClass.constructorName. It doesn't address operators or getters/setters, but it seems like there's some duplication there.
Consider this code:
class Foo<T> {
Thing t;
bool checkType(T other) {
return (other is t.runtimeType);
}
}
Will something like this be legal?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.