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

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

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

Swift Generic Type in Function + Higher Order Function

let say I have a generic method to do some conversion that takes an Input and an Output, part of the process flows required a block (which takes the generic I and O too) to process it.
func run<I : Codable, O : Codable>(_ inputType: I, _ outputType: O, _ converter : (_ input: I)-> O) throws -> Void
{
// do something to input I
let output : O = converter(input)
// do something else to output O
}
The problem is, I am unable to call this method like example:
run(InputObj, OutputObj) { (input) -> Codable in
var outputObj = OutputObj( ... )
// do something here
return outputObj
}
and it show an error:
Cannot convert value of type '() -> Codable' to
expected argument type '() -> _'
I have tried to change the block's return type "Codable" to "OutputObj" too.
Appreciate if anyone can help.
You don't need to make this a method on a class. The problem in the simplified code in your question is that the closure is explicitly typed to return Codable but in the code in your answer it typed to return OutputObj.
If you remove the explicit return type for the closure in your example — and rely on the types being inferred from the input and output arguments — the code works without further modifications.
try run(inputObj, outputObj) { input in
// do something and return an output object
}
Further, a few suggestions for Swift style improvements:
It's redundant to return Void so you can remove that from the run function declaration.
Swift usually tries to describe/name its generic parameters. For example, Optional<Wrapped>, Array<Element>, and Dictionary<Key, Value>. In your case Input and Output would be more descriptive than I and O.
Swift usually doesn't use the Type suffix in type or variable names and the arguments that you pass to this function aren't types, they're values/instances. In this case input and output would be more idiomatic names for the two arguments.
If it's clear from the argument types which one is the input and which one is the output then unlabeled arguments matches the Swift naming guidelines. Otherwise, the call site is clearer with labeled arguments (without the _ before the argument names).
With these changes the run function declaration would look something like this:
func run<Input: Codable, Output: Codable>(input: Input, output: Output, converter: (Input) -> Output) throws
OK!
I have found a way to fix this.
I create a class for function "run"
class Converter<I : Codable, O : Codable> {
func run(_ converter : (_ input: I)-> O) throws -> Void
}
then call by
let converter : Converter = Converter<InputObj, OutputObj>()
run() { (input) -> OutputObj in
var outputObj = OutputObj( ... )
// do something here
return outputObj
}
any everything works perfectly now!

Swift 4 filter closure [duplicate]

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
}

Swift 3.0 closure expression: what if the variadic parameters not at the last place in the parameters list?

Update at 2016.09.19
There is a tricky, indirect way to use variadic parameters before some other parameters in closure expression parameters list, haha
let testClosure = { (scores: Int...) -> (_ name: String) -> String in
return { name in
return "Happy"
}
}
let k = testClosure(1, 2, 3)("John")
And I found some related issues in bugs.swift.org:
SR-2475
SR-494
Original Post
According to the document of Swift 3.0, for a closure expression, "variadic parameters can be used if you name the variadic parameter"(see Closure Expresssion Syntax part). But for Swift 2.x, the description is "Variadic parameters can be used if you name the variadic parameter and place it last in the parameter list", the border part has been removed in Swift 3.0 document, is it means variadic parameter can be a argument of closure expression even it is not at the last place? If so, why the codes below can't compile successfully?
let testClosure = { (scores: Int..., name: String) -> String in
return "Happy"
}
let k = testClosure(1, 2, 3, "John") // Missing argument for parameter #2 in call
If the argument label can be used in the call, I think the compiler can compile the code above successfully, but in Swift 3.0, closure expression's argument labels are regarded as Extraneous.
Besides, Swift 3.0 document indicates that the parameters in closure expression syntax can be in-out parameters, but Swift 3.0 said that closure expression syntax can use constant parameters, variable parameters, and inout parameters. Why Apple removed descriptions like constant parameters, variable paramters, is it because in Swift 3.0, the parameters can't be var?
Thank you very much for your help!
Still in Swift3 variadic arguments have to be the last parameter in the signature, because despite the fact that in your case the last parameter typed as String can be deduced, there's some cases where not, because of the infinite expansion of variadic argument:
let foo = { (i:Int..., j: Int) -> Int in
return j
}
foo(1,2)
...in Swift 3.0, the parameters can't be var?
var params where removed in Swift3 SE-0003 to avoid confusion with inout parameters, because both var and inout params can be assigned inside function, but just inout is reflected back.
func doSomethingWithVar(var i: Int) {
i = 2 // change visible inside function.
}
func doSomethingWithInout(inout i: Int) {
i = 2 // change reflected back to caller.
}
removing var from parameter list, remove the confusion above.
Variadic parameter have to be last and according to your situation, you can type this:
let testClosure = { (_ name: String, scores: Int...) -> String in
return "Happy"
}
let k = testClosure("John", 1, 2, 3)
You are able to create a func in Swift 3.0 where the variadic parameter is NOT the last argument. For example...
func addButtons(buttons: UIButton..., completion: (() -> ())? = nil)
I believe it's because the parameter following the variadic parameter is named, and so the func does not confuse the next named argument with more variadic arguments.
addButtons(buttons: button1, button2, button3) {
//do completion stuff
}

What is the syntax for a closure argument in swift

In Swift headers, the isSeparator: argument accepts a closure
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
But in the documentation, it lists closure syntax differently
{ (parameters) -> return type in
statements
}
How are you supposed to know that (Self.Generator.Element) throws -> Bool rethrows refers to a closure / requires a closure? Are there other ways that the headers/docs might list argument as meaning a closure?
The "thing" giving away that this is a closure is the ->. The full type is
(Self.Generator.Element) throws -> Bool
It means that the closure takes a variable of type Self.Generator.Element and has to return a Bool upon some calculation based on the input. It may additionally throw some error while doing so - that is what the throws is for.
What you then write
{ (parameters) -> return type in
statements
}
would be an actual implementation, a value of some generic closure type.
The type of a closure is for example (someInt:Int, someDouble:Double) -> String:
var a : ((someInt:Int, someDouble:Double) -> String)
Once again the thing giving away that a is actually a closure is the -> in the type declaration.
Then you assign something to a via some code snippet following your second code block:
a = { (integer, floating) -> String in
return "\(integer) \(floating)"
}
You can tell by the argument's type. Everything in Swift has a type, including functions and closures.
For example, this function...
func add(a: Int, to b: Int) -> Int { return a + b }
...has type (Int, Int) -> Int. (It takes two Ints as parameters, and returns an Int.)
And this closure...
let identity: Int -> Int = { $0 }
...has type Int -> Int.
Every function and closure has a type, and in the type signature there is always a -> that separates the parameters from the return value. So anytime you see a parameter (like isSeparator) that has a -> in it, you know that the parameter expects a closure.
the isSeparator definition means (Self.Generator.Element) throws -> Bool that you will be given an Element and you should return a Bool. When you will call split, you then can do the following :
[1,2,3].split(…, isSeparator : { element -> Bool in
return false
})
This is a pure silly example but that illustrates the second part of your question