Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow instantiation of a Type object #1425

Open
jaddison06 opened this issue Feb 1, 2021 · 7 comments
Open

Allow instantiation of a Type object #1425

jaddison06 opened this issue Feb 1, 2021 · 7 comments
Labels
meta-classes request Requests to resolve a particular developer problem type-variable-constraints

Comments

@jaddison06
Copy link

jaddison06 commented Feb 1, 2021

Occasionally one needs to work with different types of object in different scenarios, frequently subclasses of the same type. I frequently have functions that I need to return them. My current solution looks like this:

class Foo {
  @override
  String toString() => "Foo!";
}

class Bar extends Foo {
  @override
  String toString() => "Bar!";
}

class Baz extends Foo {
  @override
  String toString() => "Baz!";
}

Foo instantiate(Type type) {
  switch(type) {
    case Foo: return Foo();
    break;

    case Bar: return Bar();
    break;

    case Baz: return Baz();
    break;

    default: return Foo();
  }
}

void main() {
  Type foo = Foo; // returned from some indeterminate function
  var instance = instantiate(foo);
  print(instance); // "Foo!"
}

A more elegant way of doing this, IMO, would be the ability to instantiate a Type with (), or the new keyword. Possibly we could also add a generic parameter for Type? Syntax could look like this:

void main() {
  // Foo and Bar from previous code snippet

  Type foo = Foo;
  var instance = foo(); // type: dynamic
  print(instance); // "Foo!"
  
  Type<Foo> typedFoo = Foo;
  var typedInstance = new typedFoo(); // type: Foo
  print(typedInstance); // "Foo!"
  
  // also valid!
  Type<Foo> bar = Bar;
  var barInstance = bar(); // type: Foo
  print(barInstance); // "Bar!"
}
@jaddison06 jaddison06 added the feature Proposed language feature that solves one or more problems label Feb 1, 2021
@eernstg
Copy link
Member

eernstg commented Feb 1, 2021

Note the similarity between this proposal and a very old discussion about supporting new T() where T is a type variable: dart-lang/sdk#3633, dart-lang/sdk#7021, dart-lang/sdk#24343, dart-lang/sdk#30074.

The creation of an instance of a class which is reified by a given instance of type Type is basically reflection, and Dart is not likely to support such operations outside dart:mirrors. (One main reason for this is that reflection tends to be very costly in terms of program size). dart:mirrors is only supported on some platforms.

So there are strong forces pushing you in the direction of using an approach like your function instantiate.

@jaddison06
Copy link
Author

Thank you - this makes sense! I don't know a lot about mirrors, but if anyone else comes across this I've solved it like so:

dynamic instantiate(Type type) {
  ClassMirror mirror = reflectClass(type);
  return mirror.newInstance(Symbol(''), []).reflectee;
}

@jepperaskdk
Copy link

Thank you - this makes sense! I don't know a lot about mirrors, but if anyone else comes across this I've solved it like so:

dynamic instantiate(Type type) {
  ClassMirror mirror = reflectClass(type);
  return mirror.newInstance(Symbol(''), []).reflectee;
}

How about when you have Type type and wish to create e.g. List<T> where T == type?

@jaddison06
Copy link
Author

jaddison06 commented Apr 6, 2021

Can't you just do new List<type>;? The generic parameter is of type Type, unless I'm mistaken. Alternatively, put it in the type declaration:
List<type> someList = [];

@jepperaskdk
Copy link

Can't you just do new List<type>;? The generic parameter is of type Type, unless I'm mistaken. Alternatively, put it in the type declaration:
List<type> someList = [];

Frankly not. It parses type as a literal type-name.
I think this works however:

var clsMirror = reflectType(List, [type]) as ClassMirror;
var args = <dynamic>[];
dynamic t = clsMirror.newInstance(const Symbol(''), args).reflectee;

@leafpetersen leafpetersen added meta-classes request Requests to resolve a particular developer problem type-variable-constraints and removed feature Proposed language feature that solves one or more problems labels Nov 21, 2023
@leafpetersen
Copy link
Member

Re-opening to serve as a canonical "request" issue for instantiating type variables.

@leafpetersen leafpetersen reopened this Nov 21, 2023
@lrhn
Copy link
Member

lrhn commented Nov 21, 2023

This issue seems to be about instantiating through a Type object, not a type variable. While being able to do that will also enable to instantiate through a type variable, the other direction doesn't have to hold.

"Instantiating" a type variable means invoking a constructor of the underlying class of the type represented by the type variable.

If you can do that, you can probably also invoke other static members.

The one thing that makes constructors different is that they are necessarily and automatically "self typed", so a constructor on a type variable T extends Foo will return a T, even if we don't have any notion of self-types in the type system.

Any other static method is just typed somehow, by whatever constraints we allow you to write to ensure that the type even has a static method.

Unless we introduce self-types into the type system in general.

Guess I'm trying to say that there is a big family of related, but probably not equally viable, features here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
meta-classes request Requests to resolve a particular developer problem type-variable-constraints
Projects
None yet
Development

No branches or pull requests

5 participants