Vince O'Sullivan
Vince O'Sullivan

Reputation: 2701

Why does this Swift code snippet compile? How does it work?

Today, I saw some sample Swift 2.0 (Xcode 7.2) code that can be summarised as:

let colours = ["red", "green", "blue"]
let r1 = colours.contains("The red one.".containsString)  // true
let y1 = colours.contains("The yellow one.".containsString)  // false

I would have expected a compilation error due to the lack of parenthesis on the containsString() function. In fact, I'm not even sure how the recursion is working. Are the Strings recursing through each item in the colours array or vice versa?

Any explanation appreciated.

Upvotes: 4

Views: 181

Answers (2)

Cristik
Cristik

Reputation: 32782

In Swift, functions can be considered as named closures. Quoting from Apple documentation on closures:

Global and nested functions, as introduced in Functions, are actually special cases of closures

However containsString() is an instance method (thanks Martin R for your observation). The code works, because instance methods in Swift are actually curried functions, which fall into the named closure category.

What happens is that "The red one.".containsString gets translated to a global function like this:

String.containsString("The red one.")

which returns a function (String) -> Bool, that ends up being called by contains like this:

String.containsString("The red one.")("red")
String.containsString("The red one.")("green")
String.containsString("The red one.")("blue")

Now, considering the signature of contains:

public func contains(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool

and signature of containsString:

public func containsString(other: String) -> Bool

we can see that on an array of strings the predicate parameter and the result of String.containsString("The red one.") are compatible: both expect a String as argument and return a Bool. So the compiler can happily call the curried function.

Upvotes: 5

vrwim
vrwim

Reputation: 14260

What you are actually doing is calling the method .contains(predicate: String -> Bool) (the actual method can throw, but that's not relevant here)

This means that you are asking the array colours if it contains an element that conforms to that predicate, which is "The red one.".containsString. So the array is checking its elements one by one and checking it against that predicate. If it finds one, it will return true, otherwise it will return false.

The code above does this:

"The red one.".containsString("red")
"The red one.".containsString("green")
"The red one.".containsString("blue")

"The yellow one.".containsString("red")
"The yellow one.".containsString("green")
"The yellow one.".containsString("blue")

And it checks if it got true somewhere.

Upvotes: 6

Related Questions