<div dir="ltr">I didn't really understand your answer, so I figured that I'd just experiment until I could see the pattern. Unfortunately, I found very few signs of any underlying pattern -- and I didn't even get a chance to experiment with generics. All I can see is behavior that is frighteningly inexplicable. I've tried to explain my confusion in the inline comments below. (FYI: I'm still using Swift 2.2 at the moment... if that matters.)<div><div><br></div><div>- Aaron<br><div><br><div><br></div><div><div><font face="monospace, monospace">// Trivial class hierarchy for the examples</font></div><div><font face="monospace, monospace">class C {}</font></div><div><font face="monospace, monospace">class D: C {}</font></div><div><font face="monospace, monospace">class E: D {}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">// Trivial protocol hierarchy</font><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">for the examples</span></div><div><font face="monospace, monospace">protocol P {</font></div><div><font face="monospace, monospace"> var a: Bool { get }</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace">protocol Q: P {</font></div><div><font face="monospace, monospace"> var b: Bool { get }</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace">class R: Q {</font></div><div><font face="monospace, monospace"> let a = true</font></div><div><font face="monospace, monospace"> let b = true</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">// Base protocol with some default implementations</font></div><div><font face="monospace, monospace">protocol BaseProtocol {}</font></div><div><font face="monospace, monospace">extension BaseProtocol {</font></div><div><font face="monospace, monospace"> // f1 has a more permissive parameter type than f2</font></div><div><span style="font-family:monospace,monospace"> func f1(x: Int??) -> String { return "Base" }</span><br></div><div><span style="font-family:monospace,monospace"> func f2(x: Int?) -> String { return "Base" }</span><br></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // g1 has a more permissive parameter type</font><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">than g2</span></div><div><span style="font-family:monospace,monospace"> func g1(x: C) -> String { return "Base" }</span><br></div><div><font face="monospace, monospace"> func g2(x: D) -> String { return "Base" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // h1 has a more permissive parameter type</font><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">than h2</span></div><div><span style="font-family:monospace,monospace"> func h1(x: P) -> String { return "Base" }</span><br></div><div><font face="monospace, monospace"> func h2(x: Q) -> String { return "Base" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // two functions with incomparable but overlapping parameter types</font></div><div><font face="monospace, monospace"> func v1(x: (Int?, Int)) -> String { return "Base" }</font></div><div><font face="monospace, monospace"> func v2(x: (Int, Int?)) -> String { return "Base" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // two functions with incomparable but overlapping parameter types</font></div><div><span style="font-family:monospace,monospace"> func w1(x: () -> Int?) -> String { return "Base" }</span><br></div><div><font face="monospace, monospace"> func w2(x: () throws -> Int) -> String { return "Base" }</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">// Derived protocol with some default implementations</font></div></div><div><span style="font-family:monospace,monospace">protocol DerivedProtocol: BaseProtocol {}</span><br></div><div><font face="monospace, monospace">extension DerivedProtocol {</font></div><div><font face="monospace, monospace"> // f2 has a more permissive parameter type than f1</font></div><div><span style="font-family:monospace,monospace"> func f1(x: Int?) -> String { return "Derived" }</span><br></div><div><font face="monospace, monospace"> func f2(x: Int??) -> String { return "Derived" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // g2 has a more permissive parameter type than g1</font></div><div><span style="font-family:monospace,monospace"> func g1(x: D) -> String { return "Derived" }</span><br></div><div><font face="monospace, monospace"> func g2(x: C) -> String { return "Derived" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // h2 has a more permissive parameter type than h1</font></div><div><span style="font-family:monospace,monospace"> func h1(x: Q) -> String { return "Derived" }</span><br></div><div><font face="monospace, monospace"> func h2(x: P) -> String { return "Derived" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // two functions with incomparable but overlapping parameter types</font></div><div><span style="font-family:monospace,monospace"> func v1(x: (Int, Int?)) -> String { return "Derived" }</span><br></div><div><font face="monospace, monospace"> func v2(x: (Int?, Int)) -> String { return "Derived" }</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace"> // two functions with incomparable but overlapping parameter types</font></div><div><span style="font-family:monospace,monospace"> func w1(x: () throws -> Int) -> String { return "Derived" }</span><br></div><div><font face="monospace, monospace"> func w2(x: () -> Int?) -> String { return "Derived" }</font></div><div><font face="monospace, monospace">}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">class Z: DerivedProtocol {}</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">// First we check the functions whose parameters have built-in types:</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">print(Z().f1(7)) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().f2(7)) // prints "Base"</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">// The outcome seems to demonstrate that t</span><span style="font-family:monospace,monospace">he location of the function's definition within the protocol hierarchy is irrelevant. As in Java, it appears that all accessible, applicable functions are being given equal consideration, and then </span><span style="font-family:monospace,monospace">the function whose signature has a more specific parameter type is chosen. That makes sense to me.</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">// Next we check functions whose parameters have class types:</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">print(Z().g1(E())) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().g2(E())) // prints "Derived"</font></div><div><br></div><div><div><span style="font-family:monospace,monospace">// Surprisingly, the outcome here appears to contradict the previous one: a function with a less specific type is given preference. This must be due to the fact that it is defined in the derived protocol rather than the base protocol. Does that mean we should call this case "overriding" while the previous one was "overloading"? But I can't understand why there could be any value in treating user-defined types different from built-in types. Maybe the behavior here is a bug?</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">// Now we check the same functions again using an argument with a less specific type that happens to be identical to the formal parameter type of one of the functions:</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">print(Z().g1(D())) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().g2(D())) // prints "Base"</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">// And the outcome is different than before! So, for user-defined types, is there a special rule that only applies when the formal and actual parameters have precisely the same type? Of course, we wouldn't need to call this a special case if the behavior in the second example had been the same as the first, which lends support to the idea that the second example is actually illustrating a bug... but we're not done testing yet.</span></div></div><div><span style="font-family:monospace,monospace"><br></span></div><div><div><span style="font-family:monospace,monospace">// What happens with functions whose formal parameters have protocol types?</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">print(Z().h1(R())) // prints "Derived"</span><br></div></div><div><font face="monospace, monospace">print(Z().h2(R())) // prints "Derived"</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">print(Z().h1(R() as Q)) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().h2(R() as Q)) // prints "Derived"</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">// Well it appears they are handled like class types rather than built-in types -- except that</span><span style="font-family:monospace,monospace"> the special-case behavior that was observed when the class types are identical is not coming into effect here. So, maybe the second example was not a bug? Are we making a deliberate decision to sometimes give the function definition in the derived class precedence over the first? But what exactly would we mean by "sometimes"? And why would having that sometimes-different behavior be a good thing. If we have three different elementary overload resolution schemes for the different kinds of types, what's going to happen when we try to combine those types? Or combine overloading with other language features? Like generics, etc.? Yikes!</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace">// I also decided to test the behavior for a pair of functions, both of which can be applied to the argument but neither of which can match the argument's type in a more specific way than the other. I suspected this might somehow trigger a type error, but it didn't. Instead, the definition in the derived protocol was given preference:</span><br></div><div><br></div><div><span style="font-family:monospace,monospace">print(Z().v1((0, 0))) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().v2((0, 0))) // prints "Derived"</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">// I suppose that, if a type error isn't going to be raised when neither function is a better fit than the other, then it makes sense to use the definition in the derived class as a default. Unfortunately, my final example demonstrates that we can't even count on that much:</font></div><div><font face="monospace, monospace"><br></font></div><div><span style="font-family:monospace,monospace">print(Z().w1({ return 7 })) // prints "Derived"</span><br></div><div><font face="monospace, monospace">print(Z().w2({ return 7 })) // prints "Base"</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">// Given that these last two examples exhibit different behavior, the compiler really ought to define them both as type errors. </font><span style="font-family:monospace,monospace">However, I'm even more worried by the diverging behavior of the earlier examples.</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div><br></div></div></div></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Jun 28, 2016 at 7:09 PM, Dmitri Gribenko <span dir="ltr"><<a href="mailto:gribozavr@gmail.com" target="_blank">gribozavr@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Tue, Jun 28, 2016 at 6:07 PM, Aaron Bohannon <<a href="mailto:aaron678@gmail.com">aaron678@gmail.com</a>> wrote:<br>
> AH! I wasn't familiar enough with the LazyCollectionType protocol to<br>
> realize that its map() function had a different signature.<br>
><br>
> So, presumably, if I pass a non-throwing closure, the compiler will choose<br>
> the lazy map(). However, it's not immediately obvious to me why it would be<br>
> chosen. Is it because the LazyCollectionType version of map() "shadows" the<br>
> CollectionType whenever either one could be chosen (since LazyCollectionType<br>
> extends CollectionType)? Or is it because the function with the more<br>
> restrictive argument type is always chosen when more than one version of the<br>
> function could match?<br>
<br>
</span>LazyCollectionType.map() does not shadow Collection.map(), but the<br>
type checker prefers the lazy one because LazyCollectionType refines<br>
Collection. Basically, it prefers the more refined one, but the other<br>
one is still an option.<br>
<div class="HOEnZb"><div class="h5"><br>
Dmitri<br>
<br>
--<br>
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if<br>
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <<a href="mailto:gribozavr@gmail.com">gribozavr@gmail.com</a>>*/<br>
</div></div></blockquote></div><br></div>