Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart: Use generic extension method, get NoSuchMethodError: Class 'xxx' has no instance method 'yyy'

Tags:

dart

Implement a Kotlin Standard Library extension Function let:

extension KtStd<T, R> on T {
  R let(R f(T it)) => f(this);
}

And write a CLI sum calculator (The problem cannot be simplified), expect:

input: 1 2 3 4 5 6 7 8 9
output: 45

input: apple 1.2 banana 3.4
output: 4.6

Then code:

main() {
  stdin.readLineSync(encoding: Encoding.getByName('utf-8')).split(" ")
      .map((s) => double.tryParse(s)).where((e) => e != null).fold(0, (acc, i) => acc + i)
      .let((d) => d % 1 == 0 ? d.toInt() : d)
      .let((it) => print(it));
}

When input 1 2 3, getting error message:

Unhandled exception:
NoSuchMethodError: Class 'double' has no instance method 'let'.
Receiver: 6.0
Tried calling: let(Closure: (dynamic) => dynamic)
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (file:xxx.dart:xx:xx)
#2      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

In addition, if I change the fold function into reduce, giving the same input, getting error message:

Unhandled exception:
NoSuchMethodError: Class 'int' has no instance method 'let'.
Receiver: 6
Tried calling: let(Closure: (dynamic) => void)
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      main (file:xxx.dart:xx:xx)
#2      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

The dart version is:

Dart VM version: 2.8.2 (stable) (Mon May 11 15:06:42 2020 +0200) on "windows_x64"

Why I got these error messages? How can I solve it?

like image 326
hzqelf Avatar asked May 20 '20 19:05

hzqelf


1 Answers

Dart extensions are static. They are syntactic sugar that is applied on types that are known at compilation time. This means that extensions will not work on dynamic values where the type is determined at runtime.

Your long, chained expression ends up using dynamic types probably where you aren't expecting it. One problem is that when you do .fold(0, (acc, i) => acc + i), the parameter and return types of the callback are not deduced. (See https://github.com/dart-lang/language/issues/731.) acc and the return type are thus assumed to be of type dynamic.

You can fix that by explicitly specifying a type for fold: .fold<double>(...).


In your edited version of your code, you introduced a second problem:

extension KtStd<T, R> on T {
  R let(R f(T it)) => f(this);
}

You intend for KtStd<T, R> to depend on let to constrain R, but that's backwards. let is not a legal call without first making KtStd<T, R> an extension on T. R therefore is unconstrained and is assumed to be dynamic. That then forces let to return dynamic too.

If possible, I suggest reverting to your earlier version where let is separately generic on R:

extension KtStd<T> on T {
  R let<R>(R f(T it)) => f(this);
}

You can more easily identify such errors during analysis by modifying your analysis_options.yaml file and setting:

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false
  language:
    strict-raw-types: true
like image 188
jamesdlin Avatar answered Nov 10 '22 08:11

jamesdlin