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

[SR-3639] Surprising meaning of anonymous closure parameters if not all are used #46224

Open
swift-ci opened this issue Jan 14, 2017 · 8 comments
Labels
compiler The Swift compiler in itself feature A feature request or implementation improvement

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-3639
Radar None
Original Reporter Reitzig (JIRA User)
Type Improvement
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Improvement, LanguageFeatureRequest
Assignee None
Priority Medium

md5: ce9ce5fe8b89bc9d52742e7b3f4fd47f

relates to:

  • SR-1528 Swift compiler requires closures without parameter lists to reference all arguments

Issue Description:

Compare these two expressions using reduce:

let arr = [1,2,3,4,5]
let sum = arr.reduce(0) { $0 + $1 }
let count = arr.reduce(0) { $0 + 1 }

The second one does not compile because you can not add (Int,Int) and Int. Which is true, but it is a surprising error: the natural expectation is that $0 and $1 retain the same semantics even though only one of them is used.

@swift-ci
Copy link
Collaborator Author

Comment by David Friberg (JIRA)

In a closure expression, the parameter type(s) will be inferred from context. In your inline closure examples, the type of the shorthand argument names will be inferred from the expected function type; reduce with an argument type that is a tuple ((Int, Int) here, specifically). Since you may not implicitly ignore the argument of a closure (given a contextual type that expects at least 1 argument), by providing only $0, this must be inferred as a tuple. If you modify your sum example to explicitly use a tuple (rather than two separate parameters), you can retain the same semantics in the count example, explicitly choosing not to make use the 2nd tuple member of shorthand argument.

let arr = [1,2,3,4,5]
let sum = arr.reduce(0) { $0.0 + $0.1 }
let count = arr.reduce(0) { $0.0 + 1 }

Now, you could explicitly ignore "trailing" arguments when using named arguments (as per below), but for a single supplied $0 shorthand argument, no such explicitness can be used, and it must hence be inferred to as the tuple type.

let arr = [1,2,3,4,5]
let sum = arr.reduce(0) { a, b in a + b }
let count = arr.reduce(0) { a, _ in a + 1 }

Finally note that preceding arguments to single supplied shorthand argument may be omitted for use (although available and with an inferred type) in a closure, but this does not hold for "trailing" ones.

let arr = [(1, 1, 1), (2, 2, 2)]
arr.forEach { print($2) } // OK, $0 and $1 available but not used
arr.forEach { print($1) } // error, "trailing" third argument implicitly omitted

With this discussion in mind, I believe an improvement such as this one would introduce ambiguity w.r.t. inferring shorthand argument types based on implementation details in the body of the closure vs from the expected function type.

@swift-ci
Copy link
Collaborator Author

Comment by Raphael (JIRA)

> Since you may not implicitly ignore the argument of a closure [...] by providing only $0, this must be inferred as a tuple.

I guess this is where I'm not following. What prevents us from fixing the meaning of $i to the i-th parameter of the closure? Then, $0 could never refer to the tuple of all parameters and the meaning of all $i would be clear, always, no matter which are actually used.

@swift-ci
Copy link
Collaborator Author

Comment by David Friberg (JIRA)

Ah, I didn't realize you were talking about changing the default behaviour rather than implementing even more elaborate type inference.

I believe the former will in fact be implemented in Swift 4, following the Swift evolution proposal SE-0110:

"Swift's type system should properly distinguish between functions that take one tuple argument, and functions that take multiple arguments."

This will lead to only e.g. the non-tuple shorthand arguments ($0 and $1) being allowed for reduce, as its signature will, by SE-0110, be distinguished as a two-parameter signature and not a single-tuple parameter. I believe, however, that you will still not be allowed to omit a "trailing" shorthand argument (implicitly), meanining

let count = arr.reduce { $0 + 1 }

will still not compile in Swift 4; due to the omitted out 2nd argument. I.e., you will most likely still need to explicitly use named arguments to omit trailing ones, e.g. a, _ in a + 1.

@swift-ci
Copy link
Collaborator Author

Comment by Raphael (JIRA)

Thanks for the reference; it seems indeed that SE-0110 has the potential to simplify if not solve this issue here. (And it is, indeed, a requirement – I can see that now.)

Is there a deeper reason for disallowing the omission of trailing anonymous arguments? That is to say, are you saying "I guess it will still be invalid code" or "I should still be invalid code because X"?

@swift-ci
Copy link
Collaborator Author

Comment by David Friberg (JIRA)

I can only answer that with the former; if the the current syntactic rules in this context persist into Swift 4, I guess the example above will still be syntactically invalid due to the explicitly omitted trailing argument (which is currently not allowed). I don't know the rationale for this rule, however; possibly it exists so that there's a clear-cut non-ambiguity between the choice of single-tuple or multiple parameters when using shorthand arguments, a differentiation that will naturally not be needed with the implementation of SE-0110. This is just me speculating however, so for you last question it would be interesting to get some feedback from the Swift team.

@belkadan
Copy link
Contributor

The "deeper reason" was just that (a long time ago) we didn't want you to forget that there were more arguments. It's been mildly unpopular since Swift 1, and we could consider removing that restriction.

The diagnostic is better on master:

<stdin>:3:27: error: contextual closure type '(_, Int) -> _' expects 2 arguments, but 1 was used in closure body
let count = arr.reduce(0) { $0 + 1 }
                          ^

@swift-ci
Copy link
Collaborator Author

Comment by Raphael (JIRA)

I think a warning "you did not use implicit parameter $i" makes a lot of sense.

And yes, that error message is a lot better.

@swift-ci
Copy link
Collaborator Author

Comment by Raphael (JIRA)

SE-0110 was deferred.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler The Swift compiler in itself feature A feature request or implementation improvement
Projects
None yet
Development

No branches or pull requests

2 participants