for x in y { z = do something with x array.append(z) }turns into
y.map { x in return do something with x }While this
var currentValue = initialValue for x in y { currentValue = currentValue combined with x in some way (for example, summing all the elements in an array) }becomes this:
y.reduce(initialValue) { (currentValue, x) in return currentValue combined with x in some way (for example, summing all the elements in an array) }and this
for x in y { if something(x) { array.append(x) } }ends up as
y.filter { x in return something(x) }
In each case we identify a repeated idiom, implement it once, assign it a name, and then use that name from then on. Higher order functions like these are simply expression-oriented analogues of common looping idioms. By using them we can take advantage of the multitude of benefits that immutability brings us, and make our code more concise at the same time.
Another interesting thing to consider is what things map can be applied to. Arrays, clearly. Ranges, yup, we did those. Arbitrary Sequences, sure thing.
Optionals? Actually yes. Applying map to an Optional type runs the "loop body" closure either zero times (if the Optional was nil) or one time (if it was non-nil).
Box<T>? Yup, same deal. Extracts the value from the Box, and applies the mapped operation to it.
You may be wondering if there’s some underlying commonality between these things that makes them "mappable". The answer is yes, but you can’t actually say it in Swift yet (it's called Functor in other languages like Haskell).
The "expression-oriented loop" view of map() is a simple one for people coming from Objective-C, but there's another equally valid way to view it that sheds interesting light on a way of thinking about programming.
First an aside about partial application and curried functions. Given any function taking two arguments (say, adding two numbers), a way that used to be valid to write it in Swift is this:
func addTwoInts(a: Int)(b: Int) -> Int { return a + b }
This syntax is a (now-removed) Swift shorthand for a function that takes an Int and returns a function that takes another Int and returns their sum. You'd call it like this:
let result = addTwoInts(5)(6)
Given this shorthand, we can write a very interesting alternative version of map()
func map2<T, U>(body:T -> U)(array:[T]) -> [U] { var tempArray = [T]() for i in array { tempArray.append(body(i)) } return tempArray }Which we can call like this:
let squares = map2 { number in return number * number }([1, 2, 3])or like this:
let square = map2 { number in return number * number } let squares = square([1, 2, 3])
While this is obviously somewhat less convenient (because Swift's syntax is aimed towards making multi-argument functions convenient), it reveals something interesting about map:
An alternative way of thinking about map() is that it converts ("lifts") a function taking an A and returning a B into a function taking an [A] and returning a [B]
Aside from just being really cool, there's a neat intuition here too: a loop "lifts" the block of code inside to run multiple times. map() does the same to functions. Functions that transform other functions without having to care about what they do are an amazing tool for encapsulation and code reuse, just as control flow keywords that transform control flow without having to know what it does allowed for the rise of structured programming.