What's possible with Dart Generics at runtime in Flutter

3,994 views
Skip to first unread message

Demis Bellot

unread,
Apr 11, 2018, 12:22:28 PM4/11/18
to Flutter Dev

I'm developing a serialization library that works in flutter so I'm avoiding Mirrors and would like to know what's possible with runtime generics in Dart/flutter to find out what needs to be code-gen'ed and what can be automated with generic routines. 

Firstly how can we can get the runtime time of a generic argument?

T create<T>() {
   var runtimeTypeOfT = ... ?
}

Is it possible to create a new instance of T where T has an empty constructor, e.g:

T create<T>() {
    var instance = new T();
    return instance;
}

This is possible in C# without reflection by using the `where T : new()` type constraint, how can we do this in Dart/Flutter?

Can we create a generic factory function which creates a generic list with the runtime argType? 

List createList(String argType) {
   var newGenericList = ... // create List<argType>?
   return newGenericList;
}

So that we can could use it to create generic lists like:

List<int> intList = createList("int");
List<String> stringList = createList("String");


As a current workaround/hack I'm generating a dictionary of factory constructors like:

{
  'List<String>': () => new List<String>(),
  'Map<String,int>': () => new Map<String,int>(),
}

But I would like to avoid needing to do this and would like to be able to use a generic routine if possible.

Thanks!

Kris Giesing

unread,
Apr 11, 2018, 2:32:15 PM4/11/18
to Demis Bellot, Flutter Dev
As far as I know, no, you'd need mirrors to do what you want in runtime code. It might be easier to use code generation to manage the factory constructor table rather than write them by hand, but it depends on how big the table is intended to be.

The typical pattern I've seen in Dart is for each concrete class that wants to participate in serialization to have an explicit pair of methods that either consume or emit an object of type "dynamic" that is created from reading a plain-data format (like JSON) or sent to a writer of such a format. It results in a pretty significant amount of boilerplate though.

In my ideal world, Dart would support a syntax that let a Map<String, dynamic> be supplied to any constructor's named arguments, where the String key maps to the argument name and the dynamic value maps to the argument value. I understand that Python supports a similar syntax. This would allow a natural, low-boilerplate way to achieve deserialization.  Going the other direction is less straightforward; the most direct analog would be creating a Map<String, dynamic> from the public properties (and getters) of an object, but it's not clear how valuable that would be in practice given that an object's public properties may not be sufficient information for persistence.

--
You received this message because you are subscribed to the Google Groups "Flutter Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Demis Bellot

unread,
Apr 11, 2018, 3:17:08 PM4/11/18
to Kris Giesing, Flutter Dev
So none of the above is possible in Flutter?

Going the other direction is less straightforward; the most direct analog would be creating a Map<String, dynamic> from the public properties (and getters) of an object

I've solved both serialization/deserialization of JSON into typed Dart classes using code-gen + supporting library, but I'm looking to see how much of the code-gen I can reduce and move into the supporting library.

If you're interested this is what the code-gen looks like for Dart: http://test.servicestack.net/types/dart 

Which works with the Flutter's json.encode()/json.decode() library but I'm not fond of the `Map<String, TypeInfo> _types` at the bottom which each class depends on as it breaks encapsulation of each type (e.g. being able to copy/paste individual classes) and would prefer if this didn't need to be generated.

Personally I think Flutter should respect something like @MirrorsUsed so we can mark which types we want to include Mirror support for as currently everyone who wants this ability has to implement their own mini reflection metadata system instead of using Dart's standard API.


Xavier Hainaux

unread,
Apr 11, 2018, 3:59:16 PM4/11/18
to Kris Giesing, Demis Bellot, Flutter Dev
In my ideal world, Dart would support a syntax that let a Map<String, dynamic> be supplied to any constructor's named arguments, where the String key maps to the argument name and the dynamic value maps to the argument value.

I hope we will have constructor tear-off some day (https://github.com/dart-lang/sdk/issues/10659)
So we can write:

var instance = Function.apply(MyClass.new, [arg1Value], namedArguments: {#arg2, 'arg2Value'});

This is still in the "accepted features" list

Matej The Tree

unread,
Apr 11, 2018, 4:40:40 PM4/11/18
to Flutter Dev
you can use https://github.com/dart-lang/reflectable/ which basically does what you ask, with code generation instead of built in framework feature.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.

Demis Bellot

unread,
Apr 11, 2018, 4:46:42 PM4/11/18
to Flutter Dev
I'm already doing code generation, the goal is to remove as much of the code generation into a reusable library, depending on a new code generation tool defeats the purpose.

Kris Giesing

unread,
Apr 11, 2018, 6:07:22 PM4/11/18
to Xavier Hainaux, Demis Bellot, Flutter Dev
RIght, the distinction I was trying to make is that what's an identifier in your version ( #arg2 ) would be a String in mine ( "arg2", or more likely something like data.keys[2] ).

Ian Hickson

unread,
Apr 11, 2018, 9:57:53 PM4/11/18
to Demis Bellot, Flutter Dev
On Wed, Apr 11, 2018 at 9:22 AM Demis Bellot <demis....@gmail.com> wrote:

I'm developing a serialization library that works in flutter so I'm avoiding Mirrors and would like to know what's possible with runtime generics in Dart/flutter to find out what needs to be code-gen'ed and what can be automated with generic routines. 

Firstly how can we can get the runtime time of a generic argument?

T create<T>() {
   var runtimeTypeOfT = ... ?
}

T is the runtime type of T.

try:
  void printType<T>() { print("$T"); }

(You may beed to make sure you're on the latest beta.) 


Is it possible to create a new instance of T where T has an empty constructor, e.g:

T create<T>() {
    var instance = new T();
    return instance;
}

Dart, much to my chagrin, doesn't have virtual constructors, which is what you'd need to do this. You can work around it by creating a map of types to closures that call the relevant constructors, if you're willing to go to the length of preregistering all the types you want to construct.


Can we create a generic factory function which creates a generic list with the runtime argType? 

List createList(String argType) {
   var newGenericList = ... // create List<argType>?
   return newGenericList;
}

argType here is a String. You can't go from a String to a Type. You also can't use a Type variable as a type argument, unfortunately.

You can do this, using type arguments:

   List<T> createList<T>() {
     var newGenericList = new List<T>();
     return newGenericList;
   }

You could also, as above, create a Map that goes from Types (or Strings) to closures that call the relevant constructors.

 
As a current workaround/hack I'm generating a dictionary of factory constructors like:

{
  'List<String>': () => new List<String>(),
  'Map<String,int>': () => new Map<String,int>(),
}

But I would like to avoid needing to do this and would like to be able to use a generic routine if possible.
 
You can vote for https://github.com/dart-lang/sdk/issues/3633 or https://github.com/dart-lang/sdk/issues/10667 (which seem to be basically the same) if you want to indicate to the Dart team that you'd like this feature. :-)

--

--
Ian Hickson

😸

Demis Bellot

unread,
Apr 11, 2018, 10:25:21 PM4/11/18
to Flutter Dev
Thanks for the response, answers are very helpful - much appreciated! 

Demis Bellot

unread,
Apr 11, 2018, 10:35:39 PM4/11/18
to Flutter Dev
Actually this doesn't work:

void printType<T>() { print("$T"); }

main(){
  printType<String>();
  printType<int>();
}

It just prints everything out as `dynamic`.

On a recent version of Dart: Dart VM version: 2.0.0-dev.43.0.flutter-52afcba357 (Fri Mar 30 20:33:12 2018 +0000) on "windows_x64"




--
You received this message because you are subscribed to the Google Groups "Flutter Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev+unsubscribe@googlegroups.com.

Demis Bellot

unread,
Apr 11, 2018, 10:47:46 PM4/11/18
to Flutter Dev
and because T is always dynamic, this doesn't work either:

List<T> createList<T>(){ return new List<T>(); }

main(){
  List<String> strList = createList<String>();
  List<String> strList2 = new List<String>();

  print(strList.runtimeType.toString());
  print(strList2.runtimeType.toString());
}

Which prints out:

List
List<String>

When run in a Console App, and in dartpad.dartlang.org it prints out:

JSArray
JSArray<String>

I tried looking to see if I can enable strong mode but I don't see the command line option anywhere so I'm assuming it's enabled by default in the recent preview versions of Dart 2.


Matan Lurey

unread,
Apr 11, 2018, 11:21:05 PM4/11/18
to Demis Bellot, Flutter Dev
This functionality relies on Dart 2 (which is enabled only in Flutter, and DDC, today).

In DartPad, this is not yet enabled (requires Dart2JS to fully implement Dart 2). You can follow our public milestones here:

Locally in the command-line VM, I believe you can pass `--preview-dart-2`, i.e.:
$ dart --preview-dart-2 bin/main.dart

... to try out these new semantics. They are not enabled by default for the command-line VM yet.

To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Flutter Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.

David Morgan

unread,
Apr 13, 2018, 7:00:47 AM4/13/18
to Flutter Dev
As Ian said, you need to build a map of types to constructors.

This is how built_value serialization works; as part of codegen it adds entries to that map:


If you're looking for specifics you can dig into the implementation:


Cheers

Morgan

Günter Zöchbauer

unread,
Apr 22, 2018, 10:05:36 AM4/22/18
to Flutter Dev

Ian Hickson

unread,
Apr 22, 2018, 10:21:03 PM4/22/18
to Demis Bellot, Flutter Dev
Yeah, you have to use the latest beta for this to work.

To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Flutter Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
--

--
Ian Hickson

😸

Demis Bellot

unread,
Apr 24, 2018, 9:23:27 AM4/24/18
to Flutter Dev
Thanks, I wanted to support all Dart platforms so couldn't rely on using Strong Mode behavior, so just stuck with generating a map of constructor factories for each referenced Type.

Anyway I'm happy to report that we've added first-class support for Dart and Flutter in the latest release of our .NET/.NET Core Services framework, which was the flagship feature of our latest release:


Basically all our Customers are now able to create and end-to-end typed API (for all Dart platforms) by generating Dart DTO's for their Services from just a URL, e.g:

    $ dart-ref https://www.techstacks.io

Which they can use to send typed API Requests with the same generic Service Client, which looks like:

    HelloResponse response = await client.get(new Hello(name:"flutter"));

The same source code also works in Flutter, Angular Dart and Dart VM Server Apps (with or without Strong Mode).

On a related note, I used to be an active Dart developer 5-6 years ago and was surprised that the solution for serializing JSON seems to have regressed in that time. I understand the need for AOT builds in flutter but IMO there should still be some reflection-like capabilities where possible, i.e. it would be useful if we could create a generic type from arguments at runtime, e.g:

    var itemType = String;
    var stringList = createList(itemType); //= List<String>

As currently generating factories for every permutation that uses the same generic type can get pretty verbose and this feature would help reduce the amount of code-gen required.

I know with Mono's (C#) AOT builds (i.e. for running on iOS) that if your code contained any generic List with a reference Type that you could also use reflection to create a generic list of any other reference type at runtime, presumably as it's able to reuse the same code paths. 

Also wanted to say I've been pleasantly delighted by the development experience in Flutter, it's a refreshing change from the complex tooling / slow iterations times I'm used to with native SDK tool-chains. The productivity feels on par with React Native but I've ran into several issues (which the RN dev team are indiscriminately closing) that makes me believe they've adopted an inferior approach yielding high buggy/error rates. Anyway happy that there's finally a cross-platform mobile framework with fast iteration times in a productive language that produces native ARM & fast runtime performance from a shared code-base!
Reply all
Reply to author
Forward
0 new messages