Many types can be mapped over, for example: Array, Dictionary and Optional.
Other examples could be Result, Future and Signal, and all kinds of list- and tree data structures. It's clear that there are a lot of types for which it makes sense to have map defined.
Let's call all such types Mappable types. Any Mappable type can be seen as a type that is wrapping another type:
Arrays are wrapping their Element type,
Optionals are wrapping their Wrapped type,
Dictionaries are wrapping their Element = (Key, Value) type, and so on.
Calling the map method of such a Mappable (wrapping) type simply means that we are applying a transform to its wrapped value(s) and get another instance of the same Mappable type back, now wrapping the new (transformed) value(s) which might be of the same or a new wrapped type. But note that the Mappable (wrapping) type itself is the same.
Put more succinctly:
Using a map(transform: T -> U) on an Array<T> produces an Array<U>
Using a map(transform: T -> U) on an Optional<T> produces an Optional<U>, and so on.
Here's an example of mapping an Optional to another Optional, in this case an Optional<String> to an Optional<Int>:
let o1: Optional<String> = nil
let o2: Optional<String> = "foo"
print(o1.map { $0.characters.count }) // nil
print(o2.map { $0.characters.count }) // Optional(3)
And here is an example of mapping an Array to another Array, in this case an Array<String> to an Array<Int>:
let a1: Array<String> = []
let a2: Array<String> = ["one", "two", "three"]
print(a1.map { $0.characters.count }) // []
print(a2.map { $0.characters.count }) // [3, 3, 5]
And here is an example of mapping a Dictionary to another Dictionary, in this case a Dictionary<String, String> to a Dictionary<Int, Int>:
let d1: Dictionary<String, String> = [:]
let d2: Dictionary<String, String> = ["City" : "Los Angeles", "State" : "California"]
print(d1.map { ($0.0.characters.count, $0.1.characters.count) }) // [:]
print(d2.map { ($0.0.characters.count, $0.1.characters.count) }) // [5: 10, 4: 11]
Except this last example won't work out of the box, because the Swift standard library defines Dictionary's map as returning, not a Dictionary, but ... an Array! So to make the example work, we have to write the Dictionary to Dictionary map method ourselves, something like this:
extension Dictionary {
func map<K, V where K: Hashable>(transform: (Key, Value) -> (K, V)) -> [K : V] {
var newDictionary = [K : V]()
for kv in self {
let newKv = transform(kv)
newDictionary[newKv.0] = newKv.1
}
return newDictionary
}
}
So, the standard library defines the obvious/conventional map for Array<T> and Optional<T> (ie their map method returns an Array<U> and an Optional<U>, respectively).
But the map method for Dictionary<T> returns an Array<U> instead of a Dictionary<U>, why is this?
And also, why isn't there a Mappable protocol in the standard library?
(I guess a more conventional name for that protocol would be Functor.)