Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why do I have to specify the parameters to fold? Why doesn't inference work? #34118

Closed
MichaelRFairhurst opened this issue Aug 10, 2018 · 5 comments
Labels
area-front-end Use area-front-end for front end / CFE / kernel format related issues. language-discussion

Comments

@MichaelRFairhurst
Copy link
Contributor

MichaelRFairhurst commented Aug 10, 2018

I'm disabling implicit casts in the angular analyzer plugin, and I'm surprised I had to parameterize fold here:

  List<String> getHtmlPathsReferencingHtml(String htmlPath) => _dartToHtml       
      .getFilesReferencingFile(htmlPath)                                         
      .map(_dartToDart.getFilesReferencingFile)                                  
      .fold<List<String>>(<String>[], (list, acc) => list..addAll(acc))          
      .map(_dartToHtml.getFilesReferencedBy)                                     
      .fold<List<String>>(<String>[], (list, acc) => list..addAll(acc))          
      .toList();    

Without it, I get List<dynamic> and then ultimately a complaint about a downcast.

Isn't this inferrable based on the return type of my folding function, and the value I pass into the accumulator?

@natebosch
Copy link
Member

Note that in your example you don't need the <String> on the <String>[] arguments when you are specifying the T on the call to fold.

If you don't specify T then the inferred type on your folding function is (dynamic, String) -> dynamic. The problem comes in because the function is defined to return a T.

I don't know why this is, but the following example demonstrates it more directly:

void whatTypeDidIGet<T>(T arg) => print(T);

void whatTypeDidIGet2<T>(T arg, void Function(T) f) => print(T);

void whatTypeDidIGet3<T>(T arg, T Function(T) f) => print(T);


void main() {
  whatTypeDidIGet(<String>[]); // List<String>
  whatTypeDidIGet2(<String>[], (list) => list); // List<String>
  whatTypeDidIGet3(<String>[], (list) => list); // dynamic
}

@natebosch
Copy link
Member

Ah a void Function(dynamic) is a void Function(List<String>), but a dynamic Function(dynamic) is not a List<String> Function(List<String). So the T must become dynamic. If you typed the closure as well then the T would get inferred.

@a-siva a-siva added the area-front-end Use area-front-end for front end / CFE / kernel format related issues. label Aug 14, 2018
@MichaelRFairhurst
Copy link
Contributor Author

I'm going to close this as WAI

@eernstg
Copy link
Member

eernstg commented Sep 10, 2018

I think it's worth noting that whatTypeDidIGet3(<String>[], (list) => list) actually admits the type argument List<String>: The first actual argument type matches that directly, and for the function literal (list) => list the context type would be List<String> Function(List<String>), which is a perfectly fine typing. For instance:

List<String> Function(List<String>) f = (list) => list; // OK

So the question is why inference does not select that solution to the given constraints? Of course, dynamic is also an admissible actual type argument, but List<String> is more informative and would normally be preferred.

It is possible that this is an instance of the issue targeted by #25490 'Inference should flow information between arguments in a generic function call', in which case the missing bit is that the "easy argument" <String>[] may reveal that T "should be" List<String>, but there is no mechanism in place to make that information flow into the inference for the other argument (list) => list. I believe that C# uses some heuristics to allow a certain amount of information flow between arguments during inference, and we may or may not want to take a similar approach (heuristics will always work "to some extent", and it's always possible to fiddle with them and make them a little bit better in some ways, and usually worse in others ;-).

@leafpetersen, do you have a deeper insight on why this happens?

I haven't reopened this issue because it might then quickly be reclosed as a duplicate of #25490, but I do think that the underlying topic is alive.

@leafpetersen
Copy link
Member

@eernstg Yes. It's #25490 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-front-end Use area-front-end for front end / CFE / kernel format related issues. language-discussion
Projects
None yet
Development

No branches or pull requests

5 participants