> On Jan 9, 2017, at 02:13, Charlie Monroe via swift-evolution 
> <[email protected]> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to