Both map and flatMap are defind on ImplicitlyUnwrappedOptional, but they differ (obviously) in their definition according to the documentation:
func map(f: #noescape (T) -> U) -> U!
If self == nil, returns nil. Otherwise, returns f(self!).
func flatMap(f: #noescape (T) -> U!) -> U!
Returns f(self)! iff self and f(self) are not nil.
I tried using them with a simple example:
let number: Int? = 1
let res1 = number.map { $0 + 1 }.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }.flatMap { $0 + 1 }
res1 //3
res2 //3
But they produced the same results even if number was nil.
So my question is, what is the actual difference between them if I apply map or flatMap to ImplicitlyUnwrappedOptionals? Which one should I choose over the other and when?
(Remark: The answer has been updated to reflect the syntax changes in Swift 3 and later, such as the abolishment of ImplicitlyUnwrappedOptional.)
Optional.map() and Optional.flatMap() are declared as follows (I have omitted the throws/rethrows modifiers which are irrelevant here):
func map<U>(_ transform: (Wrapped) -> U) -> U?
func flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
Let's consider a simplified version of your first example using “map”:
let number: Int? = 1
let res1 = number.map { $0 + 1 }
print(res1) // Optional(2)
number has the type Int? and the closure type is inferred as (Int) -> Int. U is Int, and the type of the return value is Int?. number is not nil, so it is unwrapped and passed 1 is passed to the closure. The closure returns 2 and map returns Optional(2). If number were nil then the result would be nil.
Now we consider a simplified version of your second example with “flatMap”:
let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)
flatMap expects a closure of type (Wrapped) -> U?, but { $0 + 1 } does not return an optional. In order to make it compile, the compiler converts this to
let res2 = number.flatMap { return Optional($0 + 1) }
Now the closure has type (Int) -> Int?, and U is Int again. Again, number is unwrapped and passed to the closure. The closure returns Optional(2) which is also the return value from flatMap. If number were nil or if the closure would return nil then the result would be nil.
So there is indeed no difference between these invocations:
let res1 = number.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }
However that is not what flatMap is meant for. A more realistic example would be
func foo(_ s : String?) -> Int? {
return s.flatMap { Int($0) }
}
print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int($0)` returns nil)
print(foo(nil)) // nil (because the argument is nil)
Generally, map takes a closure of type (Wrapped) -> U and transforms
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> Optional<U>.some(transform(wrapped))
flatMap takes a closure of type (Wrapped) -> U? and transforms
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)
Here transform(wrapped) can be Optional<U>.none as well.
If (as in your example) flatMap is called with a closure which does not return an optional then the compiler converts it to an optional automatically, and there is no difference to map anymore.
This is not possible with map() where the mapping closure has the signature (T) -> U.
That's not quite right. In my view, Martin R's answer doesn't quite get at the heart of the problem, which is that the docs do not correctly describe the difference between map and flatMap.
The difference is not what kind of closure they take. Each will happily accept a closure that produces a nonOptional or a closure that produces an Optional — despite what the docs say, and despite the difference in their declarations. All of these expressions compile:
let i : Int? = nil
let result1 = i.map {_ in "hello"} // map, closure produces nonOptional
let result2 = i.flatMap {_ in "hello"} // flatMap, closure produces nonOptional
let result3 = i.map {_ in Optional("hello") } // map, closure produces Optional
let result4 = i.flatMap {_ in Optional("hello") } // flatMap, closure produces Optional
Okay, so what's the actual difference? It's what flatMap does just in case the closure does produce an Optional: it unwraps it, thus preventing a double-wrapped Optional:
let i : Int? = nil
let result1 = i.map {_ in "hello"} // String?
let result2 = i.flatMap {_ in "hello"} // String?
let result3 = i.map {_ in Optional("hello") } // String?? // double-wrapped
let result4 = i.flatMap {_ in Optional("hello") } // String? // not double-wrapped
That is the only difference between map and flatMap.
flatMap resolves nested optionals whereas map does not.
flatmap
var temp: Int? = 3
var flag: Bool = false
print(temp.flatMap { $0 < 5 ? 1 : nil } ?? .zero)
// output: 1
map
var temp: Int? = 3
var flag: Bool = false
print(temp.map { $0 < 5 ? 1 : nil } ?? .zero)
// output: Optional(Optional(1))
[map vs compactMap vs flatMap]
Swift Optional map vs flatMap
Let's create our own simple realisation of Optional type and implement map and flatMap functions
enum CustomOptional<T> {
case none
case some(T)
public init(_ some: T) {
self = .some(some)
}
func map<U>(_ transform: (T) -> U) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: U = transform(value)
let result: CustomOptional<U> = CustomOptional<U>(transformResult) //<-- force wrap the transformResult
return result
case .none:
return .none
}
}
func flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: CustomOptional<U> = transform(value)
let result: CustomOptional<U> = transformResult
return result
case .none:
return .none
}
}
}
map - can return Optional Optional
flatMap - can flat Optional Optional to Optional
Optional.map { () -> T } -> Optional<T>
Optional.map { () -> Optional<T> } -> Optional<Optional<T>>
Optional.flatMap { () -> Optional<T> } -> Optional<T>
map: returned value of transformed function is wrapped into Optional of returned value of map function
flatMap: returned value of transformed function is the same as returned value of flatMap function
//problem
//T == Int, U == CustomOptional<String>
//map<U>(_ transform: (T) -> U) -> CustomOptional<U>
//map<CustomOptional<String>>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<CustomOptional<String>>
let result: CustomOptional<CustomOptional<String>> = CustomOptional(1).map { int in
return CustomOptional("Hello: \(int)")
}
//solution
//T == Int, U == String
//flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U>
//flatMap<U>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<String>
let result5: CustomOptional<String> = CustomOptional(1).flatMap { int in
return CustomOptional("Hello: \(int)")
}
[Swift Functor, Applicative, Monad]
Related
For the following code snippet, why do res1 and res2 have different values?
func test() {
let a: String? = nil
let b: String? = nil
let foo: (String?) -> Int = { $0 == nil ? 0 : 1}
let res1 = [a, b].compactMap { $0 }.map(foo)
let res2 = [a, b].compactMap { $0 }.map { foo($0) }
print("res1: ", res1)
print("res2: ", res2)
}
The output is
res1: [0, 0]
res2: []
This appears to be a result in how the compiler selects the types of [a, b].compactMap based on the downstream operation. You can see this by inspecting the types of the arguments as they pass through the functions:
func printType<T>(_ i: String, _ v: T) -> T {
print(i, "->", T.self)
return v
}
func test() {
let a: String? = nil
let b: String? = nil
let foo: (String?) -> Int = { $0 == nil ? 0 : 1 }
let res1 = printType("cm1", printType("a1", [a, b]).compactMap { $0 }).map(foo)
let res2 = printType("cm2", printType("a2", [a, b]).compactMap { $0 }).map { foo($0) }
print("res1: ", res1)
print("res2: ", res2)
}
test()
// a1 -> Array<Optional<Optional<String>>>
// cm1 -> Array<Optional<String>>
// a2 -> Array<Optional<String>>
// cm2 -> Array<String>
// res1: [0, 0]
// res2: []
It appears that:
In the case of res1:
Because foo takes a String, map(foo) is typed such that a String? is passed through — and for map to be receiving a String?, compactMap must be returning a [String?]
In order for compactMap to be returning a [String?], its input must be a [String??]
Although [a, b] defaults to being a [String?], the compiler can also implicitly upcast it to a [String??], so that's what it does
Hence, only one layer of optionality is removed from [String??], and each of the String? values is passed to the map and into foo
In the case of res2, the compiler isn't restricted as heavily by map { foo($0) }, since $0 can be implicitly upcast inside of the closure before being passed to foo, and this is what the compiler prefers:
$0 is a String which is upcast to String? before being passed to foo
For map to receive String values, compactMap must return [String]
Since compactMap is returning a [String], its input must be [String?], which [a, b] is, no upcasting needed
Hence, now compactMap is actually filtering the nils out of [String?] into [String], and since no values are left, foo is never even called (you can see this with more print statements inside of foo
This sort of thing is very situational in the compiler, and you happened to have found a specific case where this happens. For instance, the compiler parses the results of single-statement closures differently from multi-statement closures: if you turn compactMap { $0 } into compactMap { _ = 1 + 1; return $0 }, the closure will parsed differently, and type checking will occur in a different order, resulting in [] in both cases:
let res1 = printType("cm1", printType("a1", [a, b]).compactMap { _ = 1 + 1; return $0 }).map(foo)
let res2 = printType("cm2", printType("a2", [a, b]).compactMap { _ = 1 + 1; return $0 }).map { foo($0) }
// a1 -> Array<Optional<String>>
// cm1 -> Array<Optional<String>>
// a2 -> Array<Optional<String>>
// cm2 -> Array<Optional<String>>
// res1: [0, 0]
// res2: [0, 0]
In this case, the compiler actually ended up preferring the surprising case in both instances, since return $0 allows the compiler to upcast from String? ➡ String?? (and then filter back down from [String??] ➡ [String?])!
Either way, this seems worthy of a report on https://bugs.swift.org, since the behavior is incredibly surprising. I will be happy to add test cases and comments to the report if you go ahead and file.
I set the constant calculationError inside the structure PendingBinaryOperation
I create a variable pendingBinaryOperation of type PendingBinaryOperation
I get the error in a case statement that calculationError is an unresolved identifier. The section of the code is below.
mutating func performOperation(_ symbol:String){
if let currentOperation = operations[symbol] {
switch currentOperation {
case .nullOperation(let function, let nullDiscription):
accumulator = (function(), nullDiscription, nil)
case .constant(let value):
accumulator = (value,symbol, nil)
case .unaryOperation(let function, let unaryDescription, let unaryMemory):
if accumulator != nil {
accumulator = (function(accumulator!.digit), unaryDescription(accumulator!.description),
unaryMemory(accumulator!.digit))
}
case .binaryOperation(let binaryFunction,
let binaryDescription,
let binaryError):
if accumulator != nil {
doAPendingBinaryOperation()
resultIsPending = true
pendingBinaryOperation = PendingBinaryOperation(
mathFunction: binaryFunction,
calculationDescription:binaryDescription,
firstOperand: accumulator!.digit,
descriptionOperand:(accumulator?.description)!,
THIS LINE GIVES THE ERROR ->binaryError: calculationError)
accumulator = nil
}
case .equals:
doAPendingBinaryOperation()
resultIsPending = false
}
}
}
private var pendingBinaryOperation:PendingBinaryOperation?
struct PendingBinaryOperation {
let calculationError: (Double, Double) -> String
let mathFunction: (Double, Double) -> Double
let calculationDescription: (String, String) -> String
let firstOperand: (Double)
let descriptionOperand:(String)
func perform(with secondOperand:(Double)) -> Double {
return (mathFunction(firstOperand, secondOperand))
}
func performDecription(with secondOperan:(String)) -> String {
return (calculationDescription(descriptionOperand, secondOperan))
}
}
Based on the code you show, the constructor for PendingBinaryOperation is:
PendingBinaryOperation(calculationError: (Double, Double) -> String, mathFunction: (Double, Double) -> Double, calculationDescription: (String, String) -> String, firstOperand: (Double), descriptionOperand: (String))
but you are calling:
PendingBinaryOperation(mathFunction: (Double, Double) -> Double, calculationDescription: (String, String) -> String, firstOperand: (Double), descriptionOperand:(String), binaryError: ??)
As you can see, the order of your parameters are all wrong, and you have a parameter called binaryError that doesn't exist in the type.
I'm guessing you need to reverse binaryError: calculationError to calculationError: binaryError and re-order your parameters so they are correct.
Structs in Swift automatically generate a memberwise initializer, that's what you're using here to initialize PendingBinaryOperation. This requires that all properties be set in the initializer, including binaryError.
You're trying to pass calculationError as the value of the binaryError argument - this is not possible as calculationError is unavailable in this scope (and has no value).
If you don't want to set binaryError, you'll have to abandon using the memberwise initializer. This usually means giving add of PendingBinaryOperation properties an initial value, and using the normal init (i.e. PendingBinaryOperation()).
I know what will happen with the cos and the {"cos(\($0))"} parts but I don't understand what will happen with the {_ in nil} part.
enum Operation {
case nullaryOperation(() -> Double, () -> String)
}
var operations: Dictionary<String,Operation> = [
"cos" : Operation.unaryOperation(cos, {"cos(\($0))"}, {_ in nil})
]
func performOperation(_ symbol: String) {
if let operation = operations[symbol] {
switch operation {
case .nullaryOperation(let function, let description):
accumulator = (function(), description(), nil)
}
}
}
As #CodeDifferent mentioned {_ in nil} syntax means: ignore whatever passed into this closure and always return nil.
Consider this simple functions that gives a closure as input.
func myFunc(inputClosure: (arg: Int?) -> Int?){
var num = inputClosure(arg: 2)
print(num)
}
The output is equal to the input:
myFunc { (arg) -> Int? in
return arg
}
No matter what the input is the output is 1000:
myFunc () { _ -> Int? in
return 1000
}
no matter what the input is the output is nil:
// These are the same:
myFunc { (arg) -> Int? in nil
}
myFunc { _ -> Int? in nil
}
Is there a more elegant way to filter with an additional parameter (or map, reduce).
When I filter with a single parameter, we get a beautiful easy to ready syntax
let numbers = Array(1...10)
func isGreaterThan5(number:Int) -> Bool {
return number > 5
}
numbers.filter(isGreaterThan5)
However, if I need to pass an additional parameter to my function it turns out ugly
func isGreaterThanX(number:Int,x:Int) -> Bool {
return number > x
}
numbers.filter { (number) -> Bool in
isGreaterThanX(number: number, x: 8)
}
I would like to use something like
numbers.filter(isGreaterThanX(number: $0, x: 3))
but this gives a compile error annonymous closure argument not contained in a closure
You could change your function to return a closure which serves
as predicate for the filter method:
func isGreaterThan(_ lowerBound: Int) -> (Int) -> Bool {
return { $0 > lowerBound }
}
let filtered = numbers.filter(isGreaterThan(5))
isGreaterThan is a function taking an Int argument and returning
a closure of type (Int) -> Bool. The returned closure "captures"
the value of the given lower bound.
If you make the function generic then it can be used with
other comparable types as well:
func isGreaterThan<T: Comparable>(_ lowerBound: T) -> (T) -> Bool {
return { $0 > lowerBound }
}
print(["D", "C", "B", "A"].filter(isGreaterThan("B")))
In this particular case however, a literal closure is also easy to read:
let filtered = numbers.filter( { $0 > 5 })
And just for the sake of completeness: Using the fact that
Instance Methods are Curried Functions in Swift, this would work as well:
extension Comparable {
func greaterThanFilter(value: Self) -> Bool {
return value > self
}
}
let filtered = numbers.filter(5.greaterThanFilter)
but the "reversed logic" might be confusing.
Remark: In earlier Swift versions you could use a curried function
syntax:
func isGreaterThan(lowerBound: Int)(value: Int) -> Bool {
return value > lowerBound
}
but this feature has been removed in Swift 3.
The following function finds the second index of a given item in Array of Int:
func secondIndexOf(item: Int, inArray array: Array<Int>) -> Int? {
if let firstIndex: Int = array.indexOf(item) {
let slice: ArraySlice<Int> = array.suffixFrom(firstIndex + 1)
return slice.indexOf(item)
}
return nil
}
However, when I attempt to create a generic version of this function to find the second Equatable item, I get an error:
func secondIndexOf<T: Equatable>(item: T, inArray array: Array<T>) -> T? {
if let firstIndex: Int = array.indexOf(item) {
let slice: ArraySlice<T> = array.suffixFrom(firstIndex + 1)
return slice.indexOf(item) // Cannot invoke 'indexOf' with an argument list of type '(T)'
}
return nil
}
Why is this not valid Swift code, and what is the expected argument list if not (T)? Xcode autocomplete shows indexOf(element: Comparable) with which T should be compatible.
The compiler is giving you a confusing error message here—it isn't actually concerned about the argument. The return value is the source of the problem, since you aren't returning a value of type T, but an index of the array. You just need to change your return type to Int?:
func secondIndexOf<T: Equatable>(item: T, inArray array: Array<T>) -> Int? {
if let firstIndex: Int = array.indexOf(item) {
let slice: ArraySlice<T> = array.suffixFrom(firstIndex + 1)
return slice.indexOf(item)
}
return nil
}