[swift-evolution] Proposal: Make $0 always refer to a closure’s first argument
John McCall
rjmccall at apple.com
Tue Jan 19 22:35:40 CST 2016
> On Jan 19, 2016, at 7:20 PM, Paul Cantrell via swift-evolution <swift-evolution at swift.org> wrote:
>
> Surprisingly, this code does not compile:
>
> func foo(val: Int) { }
>
> func bar(closure: (Int,Int) -> Void) {
> closure(0, 1)
> }
>
> bar { foo($0) } // compiler error
> bar { foo($1) } // just dandy
> bar { foo($0 + $1) } // also works
>
> The compiler error is:
>
> Cannot convert value of type (Int, Int) to expected argument type Int
>
> It appears that the meaning of $0 is overloaded: it can refer either to the tuple of all arguments, or to just the first argument. The presence of another placeholder variable ($1 in the third example) seems to trigger the latter behavior.
It’s dumber than that. The type-checker assumes that the closure has a tuple of arguments ($0, $1, …, $N), where $N is the largest N seen in the closure. Thus, a two-argument closure falls down if you ignore the second argument. It’s dumb, and we’ve known about it for a long time; and yet it’s been remarkably annoying to fix, and so we haven’t yet.
Anyway, it’s a bug and doesn’t need to go through evolution.
John.
>
> This is certainly confusing. I’m posting to the list after receiving two Siesta user questions in the same day that both boil down to this issue.
>
> Even if you do understand the behavior, it’s a real nuisance: it prevents concise implementation of a multi-arg closure which wants to ignore all but its first argument. Instead, such a closure has to drop back to the more verbose syntax:
>
> bar { a, _ in foo(a) } // sigh
>
> …or use this legibility-proof workaround:
>
> bar { foo($0.0) } // yuck! wat?!
>
> (Note that this problem exists only for the first argument; a closure that wants to ignore all but the second has no such problem.)
>
> This behavior contradicts the Swift documentation, which clearly says that $0 refers to the first argument:
>
>> Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.
>
> And:
>
>> A closure may omit names for its parameters. Its parameters are then implicitly named $ followed by their position: $0, $1, $2, and so on.
>
>
> I can’t find anything in the docs that mentions this “all args tuple” behavior, so perhaps it’s a bug? Let me know if it is, and I’ll just file a bug report for it.
>
> However the “whole tuple” behavior does seem to be intentional, and preserving that while fixing the problem above appears to require a language change. Thus…
>
> Proposal
>
> The implicit closure variable $0 should always refer to the closure’s first argument, and a different implicit name — perhaps $* or $_ or $... — should refer to the all-args tuple.
>
> Thoughts?
>
> Cheers,
>
> Paul
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160119/c6e1e484/attachment-0001.html>
More information about the swift-evolution
mailing list