Home > Software design >  Generic type inference in function from lists
Generic type inference in function from lists

Time:04-10

I'm working on some generic list utility functions and there seems to be an issue with the type inference for a generic function when the primary variables are lists. This is demonstrated with the following code:

List<T> combine<T>(List<T> a, List<T> b, T Function(T a, T b) combiner) {
  final list = <T>[];
  for (int i = 0; i < a.length && i < b.length; i  ) {
    list.add(combiner(a[i], b[i]));
  }
  return list;
}

void main() {
  final a = [5, 8];
  final b = [7, -3];
  final c = combine(a, b, (a, b) => a   b); // Error

  print(c); 
  // Expected: [12, 5]
}

When I use this code as-is, the type inference within the lambda sets a and b to be Object?, which results in the following error message:

The operator ' ' can't be unconditionally invoked because the receiver can be 'null'. Try adding a null check to the target ('!').

Doing what the error message says changes the message to the following:

The operator ' ' isn't defined for the type 'Object'. Try defining the operator ' '.

The issue obviously is that type inference is assigning the parameters to Object? instead of the expected int. This can be worked around by either typing the parameters or explicitly passing the generic type to the function:

final c = combine(a, b, (int a, int b) => a   b);
// OR
final c = combine<int>(a, b, (a, b) => a   b);

However, that's an added level of verbosity that I don't want to have to force the users of these utility functions to have to do (not to mention it will be a support issue when I have to explain to them to do this). Is there a way to change the function signature to make it so type inference works as expected?

CodePudding user response:

This is basically Dart List.fold vs List.reduce type inference, but in your case you could sidestep the problem by making your function an extension method so that T is deduced from the receiver instead of from the arguments:

extension<T> on List<T> {
  List<T> combineWith(List<T> b, T Function(T a, T b) combiner) {
    final list = <T>[];
    for (int i = 0; i < length && i < b.length; i  ) {
      list.add(combiner(this[i], b[i]));
    }
    return list;
  }
}

void main() {
  final a = [5, 8];
  final b = [7, -3];
  final c = a.combineWith(b, (a, b) => a   b); // Error

  print(c);
  // Expected: [12, 5]
}
  • Related