Swift 4 filter closure [duplicate] - swift

After the gloss project for Swift 4 in Xcode 9
I am getting following error which i have no idea
Closure tuple parameter '(key: _, value: _)' does not support
destructuring
Code:
extension Dictionary
{
init(elements: [Element]) {
self.init()
for (key, value) in elements {
self[key] = value
}
}
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ (key, value) in
return try transform(key, value)
}))
}
}
Error comes at this point try flatMap({ (key, value)in

Let's start with the definition of flatMap for a dictionary which is the following:
func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
You see that the transform closure takes only one parameter of type Element where Element is just a typealias for a tuple:
public typealias Element = (key: Key, value: Value)
So the first and only argument of the closure should be a tuple of two elements (key of type Key and value of type Value).
Now, if you look at your code (which compiles in Swift 3), you will see that this is not the case, and you should be asking why does this even work in Swift 3.
try flatMap({ (key, value) in
return try transform(key, value)
})
Your closure takes 2 arguments instead of one (key of type Key and value of type Value). This works in Swift 3 thanks to a feature called destructuring where the compiler will automatically transform a tuple of 2 elements into 2 arguments.
But this feature is weird, rarely used and gives unexpected results most of the time so it has been removed in Swift 4.
Edit: As pointed out by OOPer, this feature has been temporarily removed in Swift 4 beta but should be re-added before the final version is out.
Instead you should be writing:
try flatMap({ tupleArgument in
return try transform(tupleArgument.key, tupleArgument.value)
})
And your flatMap function becomes:
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ element in
return try transform(element.key, element.value)
}))
}

It's a side-effect of this proposal for Swift 4:
SE-0110 Distinguish between single-tuple and multiple-argument function types.
But some features included in this proposal caused some regression which is addressed in this post of the evolution-announce mailing list:
[swift-evolution-announce] [Core team] Addressing the SE-0110 usability regression in Swift 4
So, you can expect in the future beta or GM version of Xcode 9, your code would compile well again. Until then, you can use this sort of workaround:
internal func flatMap<KeyPrime , ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime : ValuePrime] {
return Dictionary<KeyPrime,ValuePrime>(elements: try flatMap({ pair in
let (key, value) = pair
return try transform(key, value)
}))
}
By the way, in Swift 4, Dictionary has some new initializers which take Sequence of (Key, Value) pair. For example:
init(uniqueKeysWithValues: S)

I just encountered this error as a result of using enumerated().map():
Closure tuple parameter does not support destructuring
I typed the code:
["foo"].enumerated().map
And then pressed Enter 3 times until Xcode autocompleted the closure boilerplate.
The autocomplete seemingly has a bug that causes the above error. The autocomplete produces double-parenthesis ((offset: Int, element: String)) rather than single-parenthesis (offset: Int, element: String).
I fixed it manually and was able to continue:
// Xcode autocomplete suggests:
let fail = ["foo"].enumerated().map { ((offset: Int, element: String)) -> String in
return "ERROR: Closure tuple parameter does not support destructuring"
}
// Works if you manually replace the "(( _ ))" with "( _ )"
let pass = ["foo"].enumerated().map { (offset: Int, element: String) -> String in
return "works"
}
Possibly the result of using Xcode 10.0 beta (10L176w)

I'm using Xcode 11.1 and Swift 5, and ran into this error while using enumerated().map(). I think this example simplifies things a little, but in general this is what fixed it for me. The true error was the compiler unable to infer to return value:
// Correct Syntax
let resultModels: [ResultModel] = array.enumerated().map { index, model in
// code
}
// Results in the error Closure tuple does not support destructuring
let resultModels = array.enumerated().map { index, model in
// code
}

Related

What is the Swift compiler doing with my return type? Is it somehow casting?

I have a method:
func allRegions() -> [MappedRegion] {
return self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
}
I was frankly surprised this worked. I'm doing lazy stuff here, but it's apparently having a lazy sequence that turns into a sequence of MappedRegion be the same.
Then I was doing some poor mans timing and modified the function to read:
func allRegions() -> [MappedRegion] {
let startTime = Date()
let result = self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
self.sumRender += (Date() - startTime)
return result
}
But that created an error:
Cannot convert return expression of type 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[StatusRowItem]>.Elements, ValveAbstract?>>, ValveAbstract>.Elements, [MappedRegion]>>>' (aka 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<Array<StatusRowItem>, Optional<ValveAbstract>>>, ValveAbstract>, Array<MappedRegion>>>>') to return type '[MappedRegion]'
That was initially a surprise. I found that if I specified the return type of result as [MappedRegion] all was happy (e.g. let result:[MappedRegion] = ...).
What is going on here? I get that the original one line function is inferring the result type as [MappedRegion], so I'm probably not getting much benefits from the lazy use. But what confuses me, is that this coercion from a lazy sequence to a fixed array automagically is reminiscent of casting in C, and I thought that Swift didn't do casting?
No, there is no casting going on. There are simply two different flatMap functions being called. LazyMapSequence has two flatMap(_:) functions (well, technically four, but two are deprecated).
In your first code block, this function is inferred (because this version of flatMap has a return type that matches your allRegions function's return type):
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
And in your second code block, this function is inferred (because there is no type annotation on your local variable that's forcing it to choose the above version of flatMap):
func flatMap<SegmentOfResult>(_ transform: #escaping (Element) -> SegmentOfResult) -> LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<Base, Element>, SegmentOfResult>>> where SegmentOfResult : Sequence

Why does Swift accept code that should return a tuple, but instead returns a string?

In the following code, I would expect Xcode not to accept because I'm using a string as the return value in the body of the code instead of a tuple. But for some reason, Xcode doesn't return an error. Why is that?
func test(name: String) -> (String) {
return name
}
There's special treatment for tuples of 0 or 1 element. As far as the type system is concerned:
() is equivalent to Void()
(T) is equivalent to T

Loop works, get error when trying map-reduce

I'm new to map-reduce and wanted to play around with it a bit. I hope this question isn't too stupid.
I have this code working:
var str = "Geometry add to map: "
for element in geometryToAdd {
str.append(element.toString())
}
print(str)
Now I wanted to play around with map-reduce since I learned it recently. I rewrote it as this:
print(geometryToAdd.reduce("Geometry add to map: ", {$0.append($1.toString())}))
This gives me an error error: MyPlayground.playground:127:57: error: type of expression is ambiguous without more context. What do I do wrong?
var geometryToAdd: Array<Geometry> = []
and the class Geometry has a toString function.
Thanks for any help.
There are two similar methods:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
func reduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
You are using the first version, where $0 is immutable and the closure
must return the accumulated value. This does not compile because
append() modifies its receiver.
Using the second version makes it compile: Here $0 is mutable and
the closure updates $0 with the accumulated value.
print(geometryToAdd.reduce(into: "Geometry add to map: ", {$0.append($1.toString())}))
Make it less ambiguous :
print(geometryToAdd.reduce("Geometry add to map: ", {
$0 + $1.toString()
}))
The error comes from the fact that you can only append() to a variable sequence : $0 is an immutable String. In the loop, str was mutable : a var, not a let.
Have a look at the signature of reduce
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
nextPartialResult is a function/closure that takes two arguments and gives a result. The arguments of this function are immutable, they are not inout parameters. Only inout parameters can be modified.
Find out more on function parameter immutability here :
Function parameters are constants by default. Trying to change the
value of a function parameter from within the body of that function
results in a compile-time error.
You would be better off using joined(separator:) instead of reduce. It has better performance, and lets you put in a separator, if you wish:
print("Geometry add to map: \(geometryToAdd.map(String.init).joined(separator: "")")

Swift closure syntax

I have following code that work:
let obsScan = source.scan(0, accumulator: +)
let obsReduce = source.reduce(0, accumulator: +)
let obs = Observable.zip(obsScan, obsReduce) { scan, reduce in
return "scan - \(scan), reduce - \(reduce)"
}
I want to rewrite it, using auto complete closure syntax, and i ended up with:
let obs = Observable.zip(obsScan, obsReduce, resultSelector: { (scan, reduce) -> _ in
return "scan - \(scan), reduce - \(reduce)"
})
However, that code throw me multiple errors:
Contextual type for closure argument list expects 2 arguments, which
cannot be implicitly ignored Consecutive statements on a line must be
separated by ';' Expected expression
I can't understand why i use tab to autocomplete function with closure, and when i fill arguments i got an errors?
Whole function declared like that:
public static func zip<O1, O2>(_ source1: O1, _ source2: O2, resultSelector: #escaping (O1.E, O2.E) throws -> RxSwift.Observable.E) -> RxSwift.Observable<RxSwift.Observable.E> where O1 : ObservableType, O2 : ObservableType
I'm not sure what are you waiting for. But this should work:
let obs = Observable.zip(obsScan, obsReduce, resultSelector: { scan, reduce in
return "scan - \(scan), reduce - \(reduce)"
})

Understanding why this Swift tuple assignment isn't allowed

The following code is fine:
func intTest() -> Int? {
var res : Int
res = 5
return res
}
There is no problem returning a non-optional Int from a method with a return type of optional Int.
Now lets see what happens when the return value is a tuple of optional values:
func tupleTest() -> (Int?, Int?) {
var res : (Int, Int)
res = (5, 5)
return res
}
The return res line results in the error:
error: cannot express tuple conversion '(Int, Int)' to '(Int?, Int?)' (aka '(Optional, Optional)')
Why does Swift not allow this?
I certainly understand why this would be an error in the other direction (optional to non-optional) but why can't a tuple accept a non-optional value in the place of an optional when a non-tuple works fine?
I've seen Swift tuple to Optional assignment but that doesn't cover why you can't do the assignment, it just covers how to solve it.
The reason that's happening is because of a type mismatch. Consider the following case:
let myClosure: (Int) -> ()
let myOtherClosure: (Int?) -> ()
The type of myClosure is (Int) -> () while myOtherClosure is of type (Int?) -> () which makes them fundamentally different types, despite the similarity of the parameters. When Swift is looking at these two constants, it's evaluating the type as a whole, not the individual pieces. The same is happening with your tuple; it's looking at the tuple type signature as a whole unit, not breaking down the parameters and recognizing that they're non-optional versions of the same type.
Converting from Int to Int? works because they're the same type, one is just an optional. Going from (Int, Int) to (Int?, Int?) doesn't because the parameters of the tuple are different therefore causing the overall type to be different, hence your error.
Taking a look at one of your examples:
func tupleTest() -> (Int?, Int?) {
let first: Int = 1
let second: Int = 2
let res: (Int?, Int?) = (first, second)
return res
}
This works because even though the values are non-optional integers, the tuple type is marked as (Int?, Int?) whereas:
func tupleTest() -> (Int?, Int?) {
let first: Int = 1
let second: Int = 2
let res = (first, second)
return res
}
doesn't compile because now res is of type (Int, Int). I'm not an expert on the Swift compiler but my guess is that the way the type system works is it doesn't deconstruct each individual part of a function or a tuple and recognize that the corresponding return parameter is the same type, just an optional.