Swift Tuple Has Unexpected Print Result

swift tuple has unexpected print result

Looks like a bug to me. You don't need to put this into a separate function; just putting this:

    let foo = (5.0, 10.0)
println("foo: \(foo)")

...into viewDidLoad in an iOS project will reproduce the problem when run on a 32-bit simulator, as far as I can tell. This may explain why you can't reproduce it in a playground: I'm guessing your playground will be 64-bit. If I run the above code on a 4S simulator, it prints zeroes, if I run it in a 5S simulator, it prints the expected values.

I'd file a bug if I were you.

Decimal values in tuple give garbage values in Swift

Its a Bug . You Can Refer
This Link

Mainly we don't use whole tuple as you used.We used its Values using .0/.1 or using somename
Like

 let http200Status = (statusCode: 200, description: "OK")

println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is \(http200Status.description)")
// prints "The status message is OK”

Unexpected result due to order in Swift's pattern matching

This behavior has been corrected in beta 3. It now shows (Something, A) whatever the order is.

Constant 'spacesLeft' inferred to have type '()', which may be unexpected Swift

For the first warning, subtract returns Void, so use subtracting:

let spacesLeft = allSpaces.subtracting(playerOneMoves.union(playerTwoMoves))

For the second error, advancedBy is deprecated, you may change like this:

if spacesLeft.count > 0 {
nextMove = spacesLeft[spacesLeft.index(spacesLeft.startIndex, offsetBy: Int(arc4random_uniform(UInt32(spacesLeft.count))))]
}

Why does deconstructing a tuple from the return value of reduce cause an error?

TL; DR

This behaviour is unfortunate, but is 'working as expected' due to a combination of:

  • The constraint system favouring un-labelled tuple types over labelled tuple types when it comes to type variable binding.
  • Multi-statement closures not participating in type inference.

Why does deconstructing the tuple cause the generic type to be inferred differently? The deconstruction part should just say what to do to the result. The method call should have been interpreted on its own, and then matched against the pattern (evenSum, oddSum).

The type checker does bidirectional type inference, meaning that the pattern used can influence how the assigned expression is type checked. For example, consider:

func magic<T>() -> T { 
fatalError()
}

let x: Int = magic() // T == Int

The type of the pattern is used to infer that T is Int.

So what happens with a tuple deconstruction pattern?

let (x, y) = magic() // error: Generic parameter 'T' could not be inferred

The type checker creates two type variables to represent each element of the tuple pattern. Such type variables are used internally within the constraint solver, and each must be bound to a Swift type before the constraint system can be considered solved. Within the constraint system, the pattern let (x, y) has the type ($T0, $T1), where $T{N} is a type variable.

The function returns the generic placeholder T, so the constraint system deduces that T is convertible to ($T0, $T1). However there's no further information for what $T0 and $T1 can be bound to, so the system fails.

Okay, let's give the system a way to bind types to those type variables by adding a parameter to the function.

func magic<T>(_ x: T) -> T {
print(T.self)
fatalError()
}

let labelledTuple: (x: Int, y: Int) = (x: 0, y: 0)
let (x, y) = magic(labelledTuple) // T == (Int, Int)

This now compiles, and we can see that the generic placeholder T is inferred to be (Int, Int). How did this happen?

  • magic is of type (T) -> T.
  • The argument is of type (x: Int, y: Int).
  • The result pattern is of type ($T0, $T1).

Here we can see that the constraint system has two options, it can either:

  • Bind T to the un-labelled tuple type ($T0, $T1), forcing the argument of type (x: Int, y: Int) to perform a tuple conversion that strips it of its labels.
  • Bind T to the labelled tuple type (x: Int, y: Int), forcing the returned value to perform a tuple conversion that strips it of its labels such that it can be converted to ($T0, $T1).

(this is glossing over the fact that generic placeholders are opened into fresh type variables, but that's an unnecessary detail here)

Without any rule to favour one option over the other, this is ambiguous. Luckily however the constraint system has a rule to prefer the un-labelled version of a tuple type when binding a type. Therefore the constraint system decides to bind T to ($T0, $T1), at which point both $T0 and $T1 can be bound to Int due to the fact that (x: Int, y: Int) needs to be convertible to ($T0, $T1).

Let's see what happens when we remove the tuple deconstruction pattern:

func magic<T>(_ x: T) -> T {
print(T.self)
fatalError()
}

let labelledTuple: (x: Int, y: Int) = (x: 0, y: 0)
let tuple = magic(labelledTuple) // T == (x: Int, y: Int)

T now gets bound to (x: Int, y: Int). Why? Because the pattern type is now simply of type $T0.

  • If T gets bound to $T0, then $T0 will be bound to the argument type (x: Int, y: Int).
  • If T gets bound to (x: Int, y: Int), then $T0 will also get bound to (x: Int, y: Int).

In both cases, the solution is the same, so no ambiguity. There's no possibility of T getting bound to an un-labelled tuple type simply due to the fact that no un-labelled tuple type is introduced into the system in the first place.

So, how does this apply to your example? Well, magic is just reduce without the additional closure argument:

  public func reduce<Result>(
_ initialResult: Result,
_ nextPartialResult: (_ partialResult: Result, Element) throws -> Result
) rethrows -> Result

When you do:

let (oddSum, evenSum) = a.reduce((odd: 0, even: 0), { (result, int) in
if int % 2 == 0 {
return (result.odd, result.even + int)
} else {
return (result.odd + int, result.even)
}
})

If we ignore the closure for now, we have the same choice of bindings for Result:

  • Bind Result to the un-labelled tuple type ($T0, $T1), forcing the argument of type (odd: Int, even: Int) to perform a tuple conversion that strips it of its labels.
  • Bind Result to the labelled tuple type (odd: Int, even: Int), forcing the returned value to perform a tuple conversion that strips it of its labels such that it can be converted to ($T0, $T1).

And because of the rule to favour the un-labelled form of tuple, the constraint solver chooses to bind Result to ($T0, $T1), which gets resolved to (Int, Int). Removing the tuple decomposition works because you no longer introduce the type ($T0, $T1) into the constraint system – meaning that Result can only be bound to (odd: Int, even: Int).

Okay, but let's consider the closure again. We're clearly accessing the members .odd and .even on the tuple, so why can't the constraint system figure out that the binding of Result to (Int, Int) isn't viable? Well, this is due to the fact that multiple statement closures don't participate in type inference. This means that the closure body is solved independently to the call to reduce, so by the time the constraint system realises that the binding (Int, Int) is invalid, it's too late.

If you reduce the closure down to a single statement, this restriction is lifted and the constraint system can correctly discount (Int, Int) as a valid binding for Result:

let (oddSum, evenSum) = a.reduce((odd: 0, even: 0), { (result, int)  in
return int % 2 == 0 ? (result.odd, result.even + int)
: (result.odd + int, result.even)
})

Or if you change the pattern to use the corresponding tuple labels, as pointed out by Martin, the type of the pattern is now (odd: $T0, even: $T1), which avoids the introduction of the un-labelled form into the constraint system:

let (odd: oddSum, even: evenSum) = a.reduce((odd: 0, even: 0), { (result, int) in
if int % 2 == 0 {
return (result.odd, result.even + int)
} else {
return (result.odd + int, result.even)
}
})

Another option, as pointed out by Alladinian, is to explicitly annotate the closure parameter type:

let (oddSum, evenSum) = a.reduce((odd: 0, even: 0), { (result: (odd: Int, even: Int), int) in
if int % 2 == 0 {
return (result.odd, result.even + int)
} else {
return (result.odd + int, result.even)
}
})

Note however that unlike the previous two examples, this causes Result to be bound to (Int, Int) due to the pattern introducing the preferred type ($T0, $T1). What allows this example to compile is that fact that the compiler inserts a tuple conversion for the passed closure, which re-adds the tuple labels for its parameter.

How to detect that parameter is a tuple of two arbitrary types?

You can use Swift's baby introspection methods to get at this:

func isTuple(b: Any) -> Bool {
return reflect(b).disposition == MirrorDisposition.Tuple
}

Note that reflect is largely undocumented and may only be there as support for the playground / debugger, but as far as I know this is the only way to do this.


To achieve this you need to drill down into what reflect() gives you, which is a struct that conforms to MirrorType, which I call a reflection, for lack of a better term. You can subscript the reflection of a tuple to get reflections of the tuples members, and then get the value back out as Any. At that point you can use optional binding to safely rediscover the underlying type:

func process(value: Any) {
println("Any \(value)")
}

func process(value: String) {
println("String \(value)")
}

func processTuple(b: Any) -> Bool {
let isTuple = reflect(b).disposition == MirrorDisposition.Tuple

let r = reflect(b)
for i in 0..<r.count {
println(r[i].0) // string holding tuple part name: ".0", ".1", etc
println(r[i].1.value) // the value of that tuple part: "aa", 1.2

process(r[i].1.value) // calls process(Any)
if let val = r[i].1.value as? String {
process(val) // calls process(String)
}
}

return isTuple
}

let myString = "aa"
let myDouble = 1.2
processTuple((myString, myDouble)) //returns false

Output:

.0
aa
Any aa
String aa
.1
1.2
Any 1.2

Closure tuple does not support destructuring in Xcode 9 Swift 4

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)
}))
}

In Swift, can I use function type in tuple?

You could do:

typealias TupleType = (Int, Bool, () -> Void)
var list = [TupleType]()

Unfortunately, trying to access an item in a tuple from the array
results in Playgrounds breaking - 'Communication with the Playground service was unexpectedly interupted'. Trying the same thing in a project results in a segmentation fault. If you're getting the same problem I would recommend you use a struct instead:

struct MyStruct {
let num: Int
let bool: Bool
let closure: () -> Void

init(num: Int, bool: Bool, closure: () -> Void = {}) {
self.num = num
self.bool = bool
self.closure = closure
}
}

var list = [MyStruct]()
list.append(MyStruct(num: 1, bool: true, closure: { println("Hello") }))
list.append(MyStruct(num: 2, bool: false))


Related Topics



Leave a reply



Submit