Coder Social home page Coder Social logo

metaclasses's People

Contributors

gbracha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metaclasses's Issues

Metaclass type bound example seems wrong

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.

One more use case

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.

Yes please (and a request for virtual statics)

(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:

  • the ability to define types whose range is a class and its subclasses

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).

  • the ability to call static methods, static fields, and constructors on class types

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"
   }
  • virtual static methods and virtual static fields
   // 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.

  • virtual constructors

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);
   }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.