Why this code compiles? Explaining some Swift magic at work!

Why this code compiles? Explaining some Swift magic at work!

A feature of Swift is clarity. Swift is easy to read and easy to write. The code reads by itself and the intent is clear. However, will it compile?

What do you think this code will do?

  • execute printing "Joe Biden"
  • it will insult you!
  • Nah, it won't compile!
  • prints "President Biden"
  • Runtime crash!
class President {
    var name = "superspreader"
    func change(_ name:String) {
        self.name = name
    }
}

let president = President()
let betterChoiceWouldBe = President.change(president)

betterChoiceWouldBe("President Biden")
print(president.name)

// ???

If you are new to Swift or even not so new, you might wonder how the code below compiles. But it does! Pause for a moment and think why.

There is a bit of Swift magic at work there, and this is what I will discuss in this post.

What is going on?

So if you follow the snippet above you will see that we create a class with one property and method, which acts as a setter for the property.

Then we instantiate the class, and we pass the instance of the class to the method. And if you are following till here, you might think, wait a moment, what?

It is right! The method expects a String and I pass an instance of President instead. How is this supposed to work?

Curry is the keyword here!

An instance method is actually a curried static/class method composed of two functions — one function that takes an instance, and another function that takes the parameters of the instance method.

The idea behind currying (named after mathematician Haskell Curry) is that any function that takes multiple parameters can be transformed into a chained series of functions that take one argument each.

Suppose you have a function of type (Self, String) -> Void — it takes an instance of Self and a String, and returns nothing. If we curry this function, we get (Self) -> (String) -> (Void), i.e. a function that takes just itself and returns a second function. This second function then takes the String argument and returns nothing (Just assigns the new value of the name property). In class methods the first function is implicit. This is why the code works.

And:

If you run this in a playground you will see that this President.change(president) returns a type (String) -> () which is a closure.

Screenshot 2020-11-05 at 12.09.10.png

What are Closures?

If you need a reminder, we use closures in Swift to store functionality. In this way you can create a function and assign it to a variable, call that function using that variable, and even pass that function into other functions as parameters.

Functions used in this way are called closures, and have a special syntax. The type of the closure is the signature of the function.

To get the signature of a function, we ignore the parameter names in the declaration and take the types of its inputs and output.

Imagine a function taking a string as input and returning nothing:

func printExample(_ myString: String) { 
     print(myString) 
}

We write the parameter types in parentheses, followed by the arrow operator and the output type. This is the signature:

(String) -> ()

And here is an example of a closure taking a string as input and returning nothing. Observe how the type of the closure matches the signature of the function.

let printClosure: (String) -> () = { myString in
    print(myString) 
}

printClosure("Hello World!) // prints Hello World!

Why does it return a closure then?

In our case, the contents of betterChoiceWouldBe is not a string, but a chunk of code, a closure, which will accept a string and return nothing. You see. Functions are first-class citizens in Swift and can be assigned to a variable and executed later. And this is the case.

But there is a bit more to this, you see, there is a mechanism called "currying" and it allows gathering separately the arguments of a function. Methods in classes are just regular functions, but with an extra argument which is self, and it’s provided to the function automatically.

So when you call President.change, it returns a function or closure, that you need to call with the actual instance of self, and that in turn returns a callable reference on the method change(). Phew!

When I call betterChoiceWouldBe with the String: "President Biden", I pass the string to the closure, which will return nothing and print "President Biden".


If you like this post pls consider buying me a coffee :) buymeacoffee.com/multitudes