2

Sample source code:

void main() {
  final func = () => petOwners.selectMany(
      (petOwner) => petOwner.pets, (petOwner, petName) => {petOwner: petName});
  print(func.runtimeType);
}

final petOwners = [
  PetOwner('Higa', ['Scruffy', 'Sam']),
  PetOwner('Ashkenazi', ['Walker', 'Sugar']),
  PetOwner('Price', ['Scratches', 'Diesel']),
  PetOwner('Hines', ['Dusty']),
];

class PetOwner {
  String name;
  List<String> pets;
  PetOwner(this.name, this.pets);
}

extension IEnumerable<TSource> on Iterable<TSource> {
  Iterable<TResult> selectMany<TCollection, TResult>(
      Iterable<TCollection> Function(TSource) collectionSelector,
      TResult Function(TSource, TCollection) resultSelector) {
    return null;
  }
}

Output:

() => Iterable<Map<PetOwner, dynamic>>

That is, instead of getting the result () => Iterable<Map<PetOwner, String>> I get a rather strange result of the form () => Iterable<Map<PetOwner, dynamic>>.

In this rather simple case, the Dart compiler is not able to infer the type "TCollection".
I can’t understand what is the reason. The code is quite simple. Easier nowhere.
Nevertheless, the result is very poor. Is this a bug in the compiler or is it done according to the language specification?
If this meets the specifications, then why was this decision made? Are there really any advantages from this (in such minimization and simplification)?

P.S.
The same simplest code written in C# works without problems.

EDIT:

The problem in that the compiler cannot infer the type "TCollection" from this code (Iterable<TCollection> Function(TSource) collectionSelector):

(petOwner) => petOwner.pets

The compiler should consider this code as the following:

() => (PetOwner) => List<String>

But the compiler recognizes this code like this:

() => (PetOwner) => dynamic
mezoni
  • 10,684
  • 4
  • 32
  • 54
  • 1
    @quetzalcoatl Unfortunately, it is not. I indicated the location of the problem at the end of the question. – mezoni Apr 24 '20 at 10:04
  • whoa, indeed! I just tried `petOwners.selectMany((petOwner) => ['aa'], (petOwner, petName) => petName);` and the result was `() => Iterable` now that I wasn't expecting – quetzalcoatl Apr 24 '20 at 10:12
  • The problem is in the first method. Your decision is known, but it does not make Dart elegant. It is more like a crutch. That is, this is the case when you do extra work (but not the compiler), instead of the compiler doing all the work, as in the normal C# language. What is the difference between Dart and C# if the code looks like a two-part water drop? – mezoni Apr 24 '20 at 10:12
  • @quetzalcoatl Thank you for your experiments. It seems to me that this is not normal compiler behavior. But I would like to know the real reason why this happens. Is this a bug or a feature? – mezoni Apr 24 '20 at 10:17
  • I can't find the source, but here's more-or-less what Eric Lippert used to say about C#/CLR/etc in such moments: Nothing is for free. Every single thing in a compiler/platform had to be thought of, analyzed, designed, implemented, tested and shipped. That's a whole lot of work, and whole lot of conditions to be met, and a whole lot of money to spend. Unless something is a critical or strategic feature, it tends to be left unimplemented, nonexistent, until someone sponsors its development, or until someone dedicated writes it and donates it to the project. – quetzalcoatl Apr 24 '20 at 12:39
  • And note from me: static analysis and type checking is usually hard, especially with generic programming (C++!). Dart has no support for overloads, that would make it easier, but still it's not a piece of cake to process. I suppose unless you can find a piece in the documentation that claims this specific example should work, or that the compiler is able to process all type correllations between method call parameters, then it's not a bug, it's a missing corner case feature, which the user can solve easily by adding a type hint here or there.. for me, seems a low-prio feature that got no love. – quetzalcoatl Apr 24 '20 at 12:43
  • If there's any Dart issue tracker, I bet you're welcome to post that question there! If that indeed is a bug, then great, it will help them tidy it up. If it's a low-prio feature, it will either remain silent, or they'll tell you that.. https://github.com/dart-lang/sdk/issues I believe? – quetzalcoatl Apr 24 '20 at 12:46
  • Found it! Standard Answer for "why this obvious feature is not implemented" -> first apragraphs of https://stackoverflow.com/a/8673015/717732 :) – quetzalcoatl Apr 24 '20 at 12:48
  • 1. I disagree with the premise that this is a "fairly simple case". 2. I don't know how you determined that the problem is due to the `(petOwner) => petOwner.pets` callback. If I modify the code to pass a function with an explicit type, the type of `func` remains the same. – jamesdlin Apr 24 '20 at 18:02
  • @jamesdlin If you have enough information, why not give an answer? It’s clear that the problem is not in the argument, the fact is that the type of the parameter "collectionSelector" is the type `Iterable Function (TSource)` and the Dart compiler is not able to infer the type from any argument (mine, yours or another). – mezoni Apr 24 '20 at 19:21
  • @jamesdlin This is a "fairly simple case" because the type `TCollection` can only be inferred from a single value (from `collectionSelector` argument). Yes, there are two places where this type is used in the function signature. But the value from which the type can be infered is only one. The second place where the type can be inferred is empty. There is nothing to infer. I mean the parameter `petName` in the parameter `TResult Function (TSource, TCollection) resultSelector`. In our case, it is the argument `(petOwner, petName) => {petOwner: petName`. It doesn’t interfere with type inference. – mezoni Apr 24 '20 at 19:44
  • I didn't give an answer because I don't know why inference isn't correctly deducing `TCollection` (and thus `TResult`). You're probably better off filing a bug. – jamesdlin Apr 24 '20 at 19:56

0 Answers0