Skip to content

Variadic Generics #2532

Open
Open
@FredslundMagnus

Description

@FredslundMagnus

Case

When we get Records, a table implementation could look like this:

class InfoTable3<C1, C2, C3, D> extends StatelessWidget {
    InfoTable3({required this.columns, required this.rows});
    final Record<InfoColumn<C1>, InfoColumn<C2>, InfoColumn<C3>> columns;
    final Iterable<Record<InfoCell<C1>, InfoCell<C2>, InfoCell<C3>, data: D>> rows;
    ...
}

and be used like this:

InfoTable3( // InfoTable3<Datetime, double, SomeEnum, Data>
      columns: (
        InfoColumn("Date", toWidget: (date) => Text(date.someFormat())), // date here is inferred to be DateTime
        InfoColumn("Amount", toWidget: (amount) => CoolWidget(amount)), // InfoColumn<double> is also inferred
        InfoColumn("Type", sort: (a, b) => someSort(a, b), // both a and b will be SomeEnum
      ),
      rows: listOfData.map(
        (data) => (
          InfoCell(data.time), // InfoCell<Datetime> since data.time is Datetime
          InfoCell(data.amount), // InfoCell<double> since data.amount is double
          InfoCell(data.type), // InfoCell<SomeEnum> since data.typeis SomeEnum
          data: data,
        ),
      ),
    );
  }

This gives nice typing since we know that the compiler will make sure that our types of each cell in a row and each column match. For more info on this example see #2531.

Problem

There is a need for several InfoTableN implementations, which is a lot of repetitive code, and of course the user have to specify InfoTableN instead of just InfoTable.

Other possible solution

Metaprogramming

Metaprogramming might allow us to just write how each InfoTableN should be implemented and it would then generate a given number of implementations.

We would still have to specify the N in InfoTableN when using the table (just a minor inconvenience) but the metaprogramming might be a lot more difficult to implement or it might not support this use case. And there would still have to be generated all implementations of InfoTableN instead of just 1.

Main idea

Variadic Generics

We could specify using a class InfoTable (using normal dart + Variadic Generics) and having the number of argument in the Records be variable.

Here we would not have to generate multiple classes so we could create the table just by using InfoTable (no need to specify the N), and would not need metaprogramming which might be more complicated than the usual Dart.

In Python

Variadic Generics will be added to python 3.11 and the specs are in this PEP: https://peps.python.org/pep-0646/
Here is an example, (*iterable in python is similar to the ...iterable in Dart):

from typing import TypeVar, TypeVarTuple

D = TypeVar('D')
Cs = TypeVarTuple('Cs')

class InfoTable(Generic[D, *Cs]):
    def __init__(self, *colums: *Cs, data: D | None =None):
        pass

Example syntax in Dart:

class InfoTable<...Cs, D> extends StatelessWidget { // ...CS, a variable number of types, D is just 1 type as normal
    InfoTable({required this.columns, required this.rows});
    final Record<...InfoColumn<Cs>> columns; // 
    final Iterable<Record<...InfoCell<Cs>, data: D>> rows;
    ...
}

The idea behind the syntax is to use the same operator as in Python * however in dart the syntax is ... so we use that, and it must be used when specifying the generics as the example above: InfoTable<...Cs, D> (as in Python).

And then every time we use Cs (the name of our varadics) then it is understood as just one of the variable number of elements. and then we can put in in other types like InfoColumn and it must have a ... somewhere in front and only then will it fold out the types in a Record or other type that support a variable number of inputs (InfoTable could be used in some other code where some variadic generic ...As could be used for InfoTable<...As, D>)

Other example

Record<...Ts> waitRecord<...Ts>(Record<...Future<Ts>> tuple) async {
   // Await a Record(Future<T1>, Future<T2>, ...., Future<TN>) and return Record<T1, T2, ....., TN>
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureProposed language feature that solves one or more problems

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions