I noticed strange behavior in type System. Look at next functions
func drop() {
let a = [1, 2, 3]
let b = a.dropLast() // Array<Int>.SubSequence
handle(a: b)
}
func handle(a: [Int]) {
}
It does not compile, because b is type of ArraySlice<Element> while the function handle(a: [Int]) wants the type [Int].
BUT If we are slicing an array within handle(a: [Int]) the code compiles and works
UPD, I understand, it is static dispatcher magic, and invokes an extension on Sequence protocol. But what is the most memory efficient way to recursively slice an array?
Would you mind elaborating a bit on why the program compiles? I see both Sequence and Array have dropLast(). The former returns [Self.Element], the latter returns ArraySlice<Element>. Since the program compiles, it seems that the compiler somehow determines that it should use Sequence's method. Why?
As far as I can tell, there is no existential type involved in the program, how does the static dispatch come into play?
You just witnessed method overloading. Another example:
let a = [1, 2, 3]
let b = a.dropLast() // Collection.dropLast
let c = a.dropLast() as ArraySlice<Int> // Collection.dropLast
let d = a.dropLast() as [Int] // Sequence.dropLast
let e: [Int] = a.dropLast() // Sequence.dropLast
I can't find in the Swift Book a documentation of what overloading is, and what are the static resolution rules. In a nutshell, the compiler looks for a suitable implementation in the available overloads, with a preference for the most precise one when there is an ambiguity.
It is this preference which explains why a naked a.dropLast() is the precise Collection.dropLast. This preference disfavors the more general Sequence.dropLast. And it avoids an "Ambiguous use of dropLast()" compiler error.
EDIT: Fixed explanation by replacing Array.dropLast(_:) with Collection.dropLast(_:) (only the Collection overload has a default argument).
If you only want that it works for Array<Int> the easiest solution is to start with an ArraySlice<Int>. You can get an ArraySlice<Int> of the full array by using the [...] subscript which is a rather hidden feature of Swift:
func drop() {
let array = [1, 2, 3] // Array<Int>
let slice = array.dropLast() // ArraySlice<Int>
handle(a: array[...]) // ArraySlice<Int> of the full aray
handle(a: slice)
}
func handle(a: ArraySlice<Int>) {}
Thanks for the explanation. I did a bit more search and find Array.dropLast(_:) seems to inherit from BidirectionalCollection. The hierarchy is as below:
I usually think overloading applies to methods on the same level. So the behavior seems to indicate that Swift doesn't give method in child protocol higher priority (I know there were a lot of discussion in the forum about behaviors with regarding to protocol methods, but I didn't pay much attention to them in the past. I just avoided the pitfalls.).
If Array defines its own dropLast(_:), I guess the behavior might be different and that method would always be used. Just my guess.