dart-lang / language

Design of the Dart language
Other
2.65k stars 201 forks source link

Propagating type parameter from generic function's argument to the function call #3705

Open FireSourcery opened 5 months ago

FireSourcery commented 5 months ago

While an object with a generic parameter preserves that type value, it is difficult to call a function passing/propagating that generic parameter. A work around is the call the function from the object. This seems quite tedious, surely there is, or can be a better way?

typedef GenericFunction<R, A> = R Function<G>(A args);

// in AppSetting class
R callTyped<R, A>(GenericFunction<R, A> fn, A args) => fn<T>(args);

void onSetting<T>(AppSetting<T> setting) {
  print(T);
  print(setting.runtimeType);
}

void main() {
  test('test', () {
    for (final setting in AppSetting.values) {
      onSetting(setting); // prints dynamic, AppSetting<double>
      setting.callTyped<void, AppSetting>(<G>(args) => onSetting<G>(setting as AppSetting<G>), setting);  // prints double, AppSetting<double>
    }
  });
}
lrhn commented 5 months ago

Sound like what you want is to extract the type variable from an AppSetting object.

There is no way to do that without the cooperation of the AppSetting class. Type variables are not accessible outside of their lexical scope, meaning outside of the class that declared them.

If the AppSetting class had a method like

  R callWith<R>(R Function<T>(AppSetting<T>) callback){
  return callback<T>(this);
}

then you could use that to get access to the actual type argument that was passed to the AppSetting constructor.

The other alternative that has been suggested, is to allow destructuring to extract type variables, fx like:

  AppSetting<final T> appSetting = someAppSetting;

which would extract the actual type argument of AppSetting that someAppSetting implements.

FireSourcery commented 5 months ago

Sound like what you want is to extract the type variable from an AppSetting object.

There is no way to do that without the cooperation of the AppSetting class. Type variables are not accessible outside of their lexical scope, meaning outside of the class that declared them.

If the AppSetting class had a method like

  R callWith<R>(R Function<T>(AppSetting<T>) callback){
  return callback<T>(this);
}

then you could use that to get access to the actual type argument that was passed to the AppSetting constructor.

The other alternative that has been suggested, is to allow destructuring to extract type variables, fx like:

  AppSetting<final T> appSetting = someAppSetting;

which would extract the actual type argument of AppSetting that someAppSetting implements.

Ah, yes a simple callback providing access to the type parameter would be sufficient. There's no need to handle arguments, as the initial context can directly pass arguments to the body of the anonymous function.

return setting.callWith<SettingMenu<T>>(<G>() => SettingMenu<G>(setting: setting as Setting<G>, settingsController: settingsController) as SettingMenu<T>);