In an appendix to his article Revenge of the Nerds, Paul Graham suggests a problem to solve in a programming language to see how “powerful” that language is.1
The problem: Write a function foo that takes a number n and returns a function that takes a number i, and returns n incremented by i.
Also note,
(That’s incremented by, not plus. An accumulator has to accumulate.)
Solving this problem is one of the first things I try when learning a new language. This article will implement various versions this function in Swift, as a way to explore some of Swift’s features.
Here’s a first attempt. It’s very similar to the makeIncremetor
function in the Closure section of the Apple Swift book, except adapted to match Graham’s definition of the problem:2
func foo(n: Int) -> (Int) -> Int { var acc = n func inc(i: Int) -> Int { acc += i return acc } return inc }
Read that first line defining foo
as: it takes a parameter, n
, and returns a function, which itself takes an Int
as a parameter and returns an Int
.3
That returned function is a closure – a self-contained combination of a function and an environment of variables (in this case, just one, acc
). Each time you then call that returned function, it adds the value i
you pass in to acc
to keep a running total, and then returns the latest total.
In case you’re not familiar with closures, here’s a short explanation of what’s happening.4 When declared, the function inc
“captures” the outer variable acc
, so instead of acc
being destroyed when it falls out of scope, it sticks around and continues to be useable by the inc
function. Note that if you call foo
again, it creates a brand new inc
function/acc
variable pairing, starting from whatever n
you just passed in. Think of this as similar to creating a new instance of a class with a member variable acc, initialized with n
.
So, people have been enjoying comparing swift to other languages. I like Ruby, so let’s take the Ruby example from Graham’s list of canonical solutions to his problem.
def foo (n) lambda {|i| n += i } end
Well that’s a lot more compact!5 Let’s look at why, and get our Swift example closer.
The Ruby example just captures and uses the input parameter n
for its state variable. We can do this in Swift too, with the addition of the var keyword in front to allow it to vary (without those changes affecting the caller’s passed-in variable):
func foo(var n: Int) -> (Int) -> Int { func inc(i: Int) -> Int { n += i return n } return inc }
Next, the inner function is anonymous, and returned directly. We can do that in Swift too. Unlike in Ruby, we don’t need to use a lambda keyword to declare an anonymous function:
func foo(var n: Int) -> (Int) -> Int { return { (i: Int) -> Int in n += i return n } }
Now let’s look at all those type declarations cluttering up the place. Ruby doesn’t have them because it is duck typed. Swift on the other hand is strongly typed.6 But there is hope – some of them can be eliminated by Swift’s type inference. In the inner function, we already know the types of everything being passed in or returned, so we can leave them off and the compiler infers them from the context:
func foo(var n: Int) -> (Int) -> Int { return { i in n += i; return n } }
Do we still need the declaration of i
? If the types can be deduced, you can use $0
, $1
etc for the arguments, and skip the in
.
func foo(var n: Int) -> (Int) -> Int { return { n += $0; return n } }
Finally, so long as a closure is just a single expression, you don’t need an explicit return – the result of the expression is automatically returned. This leaves us with something very similar to Ruby, where the last statement is always returned (in Ruby’s case, even with multi-line functions).
func foo(var n: Int) -> (Int) -> Int { return { n += $0 } }
Except oops, no, that last one won’t compile. “Could not find an overload for ‘+=’ that accepts the supplied arguments” it says. That’s a little confusing, because the “argument” in question is actually the return type. In Swift, +=
doesn’t return a value7 (unlike in Ruby where almost every statement has a value8 and can be used on the right-hand side, even an if statement).
If you were thinking, I know, let’s override +=
to return a value, nope Swift won’t let you do that.9 Also, you’re the reason I hate other people’s code. To get inspiration for an alternative function, let’s look at the lisp example from the canonical list:
(defun foo (n) (lambda (i) (incf n i)))
And here is an implementation of that function:
func incf(inout lhs: Int, rhs: Int) -> Int { lhs += rhs return lhs }
Note the use of the inout
keyword to indicate that the changes made to this parameter affect the variable passed in by the caller, unlike with var
.
Since our new function returns a result, we can now use it to implement a single-expression closure:10
func foo(var n: Int) -> (Int) -> Int { return { incf(&n, $0) } }
Swift requires you to put an & in front variable when passing in an inout
parameter, which is nice as it means you can’t accidentally miss the possibility of side-effects.
That’s probably as far as we can go. It’s pretty similar to the Ruby version at this point. Except… we didn’t quite solve the stated problem. To find out why, read on to Part 2 of this article.
- If you take issue with this as a measure of language power, take it up with Mr Graham.#160;↩
-
If you’re eyeing
foo
suspiciously, and wondering why it isn’t called, say,makeAccumulator
, I agree with you. ↩ - I’m not wild about the Swift syntax for type definitions of functions returned from functions. I know it’s completely unambiguous if you read it from left to right but it makes me nervous when I look at it. I don’t have any better proposals, either. ↩
- Those who are familiar – please be gentle with me and my explanation! ↩
- Note I said compact, not neater or cleaner or more expressive. But I happen to think it’s those things too. ↩
- Terms like strong or duck typing are not rigorously defined, but people can get very emotional about it when they think they’re being used incorrectly. Please email Casey. ↩
-
I do wonder why they decided to not have assignment return the value of the left-hand side. Seems like this is a legit use case. I guess maybe they they thought it was too side-effecty to change a value while also using it? But if you’re going to do that, go all the way and ban
++i
from being used on the right-hand side of expressions – but that would probably lead to the C guys assaulting the castle with torches and pitchforks. ↩ - That’s right, almost everything. Not everything. Grrr. Though, otherwise, I really like this language feature. ↩
-
Interestingly, it will allow you define
+=
return a value when you write it for your own classes. This is probably not a good idea. ↩ - Yes I’m aware writing a two-line function just to compact less code elsewhere is a bit silly, but give me a break, we’re just experimenting here. Plus I think the lisp “build up the language to meet your problem” thing is a nice idea. ↩
[…] Part 1 of this post, we wrote an implementation of Paul Graham’s accumulator problem in Swift. […]
You can go a little further. As you said, if a closure or function contains only one expression, that is automatically the return value without needing an explicit `return` keyword. So you can do this:
func foo(var n: Int) -> (Int) -> Int {
{ n += $0; return n }
}
Personally, I prefer not to use an `incf` function because that feels like we’re trying to simulate Ruby’s side-effect-ful `+=` operator without any good reason. But I do like the above three lines; very succinct.
Are you sure functions support the same single-expression means no return needed feature that closure expressions do? If I cut and paste that statement into a playground, I get a compiler error: Braced block of statements is an unused closure.
Of course, you can not bother with the func keyword at all, and do this, which amounts to the same thing:
That works. The Scheme programmer in me thinks that’s pretty cool!
[…] in this article, try reading some more advanced articles that involve closures on this blog – Implemeting an Accumulator in Swift, and Implementing Ruby’s ||= in […]
[…] This is part of a series on solving Paul Graham’s accumulator problem in Swift. You can find Part 1 here. […]
Nice progression, and thanks for publishing this blog. Note that in a couple of your examples you accidentally switched to returning i instead of n.
Oops, thanks for the spot, and for the feedback.