[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