[swift-evolution] Closures from methods with default args

Chris Lattner clattner at nondot.org
Fri Jan 20 00:38:02 CST 2017


On Jan 19, 2017, at 9:26 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hmm, I don't recall the earlier discussion, but IMO, Charlie's proposal is pretty sensible. Seems backwards to adding much broader things like default argument support for protocols motivated by a use case that should Just Work(TM).
> 
> I recall that once upon a time Chris Lattner declared that the core team was perfectly willing to implement difficult things if it improved the Swift user experience. Here, it seems either this is something that *can* be made to just work in the default arguments handling department, and then it should be, or it can't, and then the closure syntax is a fairly obvious and workable if not pretty workaround. No point in designing features as a workaround for something that has both an obvious ideal solution and a current workaround.

Yeah, I agree with Xiaodi on this.  This is something that should “just work” and only fails due to implementation limitations.  In principle, we should make partial applications of methods (i.e. like "value.method(x:)”) be as similar to an explicit closure as is reasonable (e.g. “{ value.method(x: $0) }”).  This means that default arguments should work in this case.

-Chris


> On Thu, Jan 19, 2017 at 23:07 David Sweeris via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
> On Jan 9, 2017, at 02:13, Charlie Monroe via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
>> I came across something that I'm not sure it's a bug or by design and if it's by design, whether this should be discussed here.
>> 
>> Example:
>> 
>> class Foo {
>>     init(number: Int) { /* ... */ }
>> }
>> 
>> let closure = Foo.init(number:) // (Int) -> Foo
>> [1, 2, 3].map(closure) // [Foo, Foo, Foo]
>> 
>> This works great until the initializer gets a default argument:
>> 
>> class Foo {
>>     init(number: Int, string: String = "") { /* ... */ }
>> }
>> 
>> // Error: Foo has no member init(number:)
>> let closure = Foo.init(number:) 
>> 
>> I was wondering if we could get closures to methods without the default arguments. Currently, this needs to be worked around by e.g. creating a second closure that invokes the method without the default arguments:
>> 
>> let closure: (Int) -> Foo = { Foo(number: $0) }
>> 
>> But to me it seems like something that should work "out of the box".
>> 
>> Thoughts?
> 
> IIRC, this issue was raised a while ago, and as best as I recall the gist of the answer was that default arguments are implemented at the call site, and because of that you can't pass a function with default arguments to something expecting a function with fewer arguments even though the two calls look identical in the source code.
> 
> It causes other issues, too. For instance, if we have
>     protocol Initable { init() }
> And
>     struct Foo { init(_ x: Int = 0) {} }
> We're left in an odd situation where `Foo`  can't meaningfully conform to `Initable` because while "init(_: Int = 0)" is not the same as "init()", if you add a "init()" to `Foo`
> you'll get an ambiguous somethingerather error because there's no mechanism for the compiler to know whether you want the actual "0 argument" function or the "1 argument with 1 default value" function.
> 
> Aside from re-architecting the default argument system (which I'm not even sure is possible, let alone a good idea), I think I see couple ways forward for the protocol conformance issue. Both have downsides, though.
> 
> 1) Require any potentially conflicting protocol functions to be in an extension so the compiler knows what's going on, have "Foo()" call the one defined in the type, and use "(Foo as Initable)()" for the protocol version defined in an extension. This could get real confusing real fast if people don't realize there's two functions with, as far as they can tell, the same signature.
> 
> 2) Add default argument support to protocols. The syntax that makes sense to me would be something like
>     protocol Bar {
>         func baz(_: Int = _)
>     }
> On the downside, I suspect this would necessarily add a phantom "Self or associated type requirement" so that the compiler could have a way to get at each implementation's default value. It's not ideal... You'd get an error kinda out of the blue if you tried to use the function non-generically, but at least you couldn't have a function change out from under you.
> 
> - Dave Sweeris 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> 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/20170119/075c94c9/attachment.html>


More information about the swift-evolution mailing list