Why How to Only Append an Array of Tuples Using Tuple Names Directly If The Values Are 'Let' Constants - Swift

Why can I only append an array of tuples using tuple names directly if the values are `let` constants -- Swift

swiftc -dump-ast unveils some possible reasons.

Let's start with simplified code:

let string = "Hello"
let num = 42
var varString = "Hi Again"
var varNum = 234

typealias SomeTuple = (string: String, int: Int)
func foo(x:SomeTuple) {}

Error cases

foo(string: "Hello", int: 42)
  (top_level_code_decl
(brace_stmt
(call_expr type='()' location=test.swift:9:1 range=[test.swift:9:1 - line:9:29]
(declref_expr type='(SomeTuple) -> ()' location=test.swift:9:1 range=[test.swift:9:1 - line:9:1] decl=test.(file).foo@test.swift:2:6 specialized=no)
(tuple_expr type='<<error type>>' location=test.swift:9:4 range=[test.swift:9:4 - line:9:29] names=string,int
(string_literal_expr type='<<error type>>' location=test.swift:9:13 range=[test.swift:9:13 - line:9:13] encoding=utf8 value="Hello")
(integer_literal_expr type='<<error type>>' location=test.swift:9:27 range=[test.swift:9:27 - line:9:27] value=42))))

In this case, the compiler cannot infer final type of string_literal_expr and integer_literal_expr. string_literal_expr can be String, StaticString, Selector or such.

foo(string: varString, int: varNum)
  (top_level_code_decl
(brace_stmt
(call_expr type='()' location=test.swift:10:1 range=[test.swift:10:1 - line:10:35]
(declref_expr type='(SomeTuple) -> ()' location=test.swift:10:1 range=[test.swift:10:1 - line:10:1] decl=test.(file).foo@test.swift:2:6 specialized=no)
(tuple_expr type='(string: @lvalue String, int: @lvalue Int)' location=test.swift:10:4 range=[test.swift:10:4 - line:10:35] names=string,int
(declref_expr type='@lvalue String' location=test.swift:10:13 range=[test.swift:10:13 - line:10:13] decl=test.(file).varString@test.swift:6:5 direct_to_storage specialized=no)
(declref_expr type='@lvalue Int' location=test.swift:10:29 range=[test.swift:10:29 - line:10:29] decl=test.(file).varNum@test.swift:7:5 direct_to_storage specialized=no))))

In this case, the compiler interpreted (string: varString, int: varNum) as (string: @lvalue String, int: @lvalue Int). And it does not match with (string: String, int: Int).

Success cases

foo(string: "Hello" as String, int: 42 as Int)
  (top_level_code_decl
(brace_stmt
(call_expr type='()' location=test.swift:13:1 range=[test.swift:13:1 - line:13:46]
(declref_expr type='(SomeTuple) -> ()' location=test.swift:13:1 range=[test.swift:13:1 - line:13:1] decl=test.(file).foo@test.swift:2:6 specialized=no)
(tuple_expr type='(string: String, int: Int)' location=test.swift:13:4 range=[test.swift:13:4 - line:13:46] names=string,int
(coerce_expr type='String' location=test.swift:13:21 range=[test.swift:13:13 - line:13:24] writtenType=String
(call_expr implicit type='String' location=test.swift:13:13 range=[test.swift:13:13 - line:13:13]
(constructor_ref_call_expr implicit type='(_builtinStringLiteral: RawPointer, byteSize: Word, isASCII: Int1) -> String' location=test.swift:13:13 range=[test.swift:13:13 - line:13:13]
(declref_expr implicit type='String.Type -> (_builtinStringLiteral: RawPointer, byteSize: Word, isASCII: Int1) -> String' location=test.swift:13:13 range=[test.swift:13:13 - line:13:13] decl=Swift.(file).String.init(_builtinStringLiteral:byteSize:isASCII:) specialized=no)
(type_expr implicit type='String.Type' location=test.swift:13:13 range=[test.swift:13:13 - line:13:13] typerepr='<<IMPLICIT>>'))
(string_literal_expr type='(_builtinStringLiteral: Builtin.RawPointer, byteSize: Builtin.Word, isASCII: Builtin.Int1)' location=test.swift:13:13 range=[test.swift:13:13 - line:13:13] encoding=utf8 value="Hello")))
(coerce_expr type='Int' location=test.swift:13:40 range=[test.swift:13:37 - line:13:43] writtenType=Int
(call_expr implicit type='Int' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37]
(constructor_ref_call_expr implicit type='(_builtinIntegerLiteral: Int2048) -> Int' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37]
(declref_expr implicit type='Int.Type -> (_builtinIntegerLiteral: Int2048) -> Int' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37] decl=Swift.(file).Int.init(_builtinIntegerLiteral:) specialized=no)
(type_expr implicit type='Int.Type' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37] typerepr='<<IMPLICIT>>'))
(tuple_expr implicit type='(_builtinIntegerLiteral: Int2048)' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37] names=_builtinIntegerLiteral
(integer_literal_expr type='Int2048' location=test.swift:13:37 range=[test.swift:13:37 - line:13:37] value=42)))))))

Explicit cast causes coerce_expr. And it constructs String and Int correctly.

 foo(string: string, int: num)
  (top_level_code_decl
(brace_stmt
(call_expr type='()' location=test.swift:12:1 range=[test.swift:12:1 - line:12:29]
(declref_expr type='(SomeTuple) -> ()' location=test.swift:12:1 range=[test.swift:12:1 - line:12:1] decl=test.(file).foo@test.swift:2:6 specialized=no)
(tuple_expr type='(string: String, int: Int)' location=test.swift:12:4 range=[test.swift:12:4 - line:12:29] names=string,int
(declref_expr type='String' location=test.swift:12:13 range=[test.swift:12:13 - line:12:13] decl=test.(file).string@test.swift:4:5 direct_to_storage specialized=no)
(declref_expr type='Int' location=test.swift:12:26 range=[test.swift:12:26 - line:12:26] decl=test.(file).num@test.swift:5:5 direct_to_storage specialized=no))))

let constants are String and Int, it does not have @lvalue. So they can be applied AS IS.

foo((string: varString, int: varNum))
  (top_level_code_decl
(brace_stmt
(call_expr type='()' location=test.swift:15:1 range=[test.swift:15:1 - line:15:37]
(declref_expr type='(SomeTuple) -> ()' location=test.swift:15:1 range=[test.swift:15:1 - line:15:1] decl=test.(file).foo@test.swift:2:6 specialized=no)
(paren_expr type='(SomeTuple)' location=test.swift:15:5 range=[test.swift:15:4 - line:15:37]
(tuple_expr type='(string: String, int: Int)' location=test.swift:15:5 range=[test.swift:15:5 - line:15:36] names=string,int
(load_expr implicit type='String' location=test.swift:15:14 range=[test.swift:15:14 - line:15:14]
(declref_expr type='@lvalue String' location=test.swift:15:14 range=[test.swift:15:14 - line:15:14] decl=test.(file).varString@test.swift:6:5 direct_to_storage specialized=no))
(load_expr implicit type='Int' location=test.swift:15:30 range=[test.swift:15:30 - line:15:30]
(declref_expr type='@lvalue Int' location=test.swift:15:30 range=[test.swift:15:30 - line:15:30] decl=test.(file).varNum@test.swift:7:5 direct_to_storage specialized=no)))))))

In this case, compared to foo(string: varString, int: varNum), load_expr is inserted, and it converts @lvalue String to String, and @lvalue Int to Int

Tuple containing an array (and assigned as variable) not working?

This should work:

let arrayOfObjects : [myObject] = []
tuples.append(letter:"t", objects: arrayOfObjects)

As you can see, arrayOfObjects is a constant. The problem here might stem from the fact that append expects a constant parameter T, while you are passing a tuple containing a var.
IMHO this is what makes append goes a little crazy, and the compiler gives a crazier error description ;-)

Problems mixing Arrays, Tuples and Closures in Swift

2 and 3's ambiguity seems to be due to Swift compiler bugs, as is using menuItems[0].closure directly which causes compiler segmentation faults.

1 works fine once I remove the parentheses for the tuple, which also seems non-logical.

menuItems.append(title: "any", closure: {})

Anyway I expect this to change/break in future Swift/compiler updates.

Swift - Joining two tuple arrays based on their values

let tuple1 = [("score1", "index1"), ("score2", "index2"), ("score3", "index3")]
let tuple2 = [("date1", "index1"), ("date2", "index2"), ("date3", "index4")]

let t2Dict = tuple2.reduce(into: [String:String]()) { (dict, args) in
let (date, index) = args
dict[index] = date
}

let tuple3 = tuple1.compactMap { args -> (String, String)? in
let (score, index) = args
guard let date = t2Dict[index] else { return nil }
return (score, date)
}

It's not as pretty as the others, but it's far more efficient to collapse one of the tuples into a dictionary first.

Is there any way to append an element/value to a tuple?

No. Each tuple with a different number of elements or a different type of elements represents a different type in the Swift type system. Hence, once you create a tuple, you cannot append elements to it, because that would change the type of the tuple.

Some really basic examples of tuples and their types:

let tupleWithTwoInts = (1,2) //has type (Int,Int)
let tupleWithThreeInts = (1,2,3) //has type (Int,Int,Int)
let tupleWithTwoStrings = ("a","b") //has type (String,String)
let tupleWithIntAndString = (1,"a") //has type (Int,String)
let tupleWithStringAndInt = ("a",1) //has type (String,Int)

Even the order of the elements make a difference in the type of a tuple.

type(of: tupleWithIntAndString) == type(of: tupleWithStringAndInt) //false

Assigning tuple to an array

You have to initialise the new property of type Properties you are creating:

    circleCollection.append(Properties(properties: (coordinatesXY: CGPoint(x: 0, y: 0), radius: circleRadius, group: "test", fillingColor: mainThemeColor)))

Also, the initializer for CGPoint has a x and y.

Consider if you actually want Properties to have only 1 property with all its fields using a tuple, or if it would be better to have something like:

struct Properties {
let coordinatesXY: CGPoint
let radius: CGFloat
let group: String
let fillingColor: UIColor
}

Are there any possible explicit uses of instances (values) of empty tuples (), i.e., of instances of typealias 'Void'?

There are lots of places that () can be useful when playing around with "CS" problems, which often have the form "implement X using Y even though you really already have X." So for instance, I might say, implement Set using Dictionary. Well, a Dictionary is a Key/Value pair. What should the type of the Value be? I've actually seen this done in languages that have Dictionaries but not Sets, and people often use 1 or true as the value. But that's not really what you mean. That opens up ambiguity. What if the value is false? Is it in the set or not? The right way to implement Set in terms of Dictionary is as [Key: ()], and then you wind up with lines of code like:

set[key] = ()

There are other, equivalent versions, like your Optional<()>. I could also implement integers as [()] or Set<()>. It's a bit silly, but I've done things like that to explore number theory before.

That said, these are all almost intentionally impractical solutions. How about a practical one? Those usually show up when in generic programming. For example, imagine a function with this kind of form:

func doThingAndReturn<T>(retval: T, f: () -> Void) -> T {
f()
return retval
}

This isn't as silly as it sounds. Something along these lines could easily show up in a Command pattern. But what if there's no retval; I don't care about the return? Well, that's fine, just pass a () value.

func doThing(f: () -> Void) {
doThingAndReturn((), f: f)
}

Similarly, you might want a function like zipMap:

func zipMap<T, U>(funcs: [(T) -> U], vals: [T]) -> [U] {
return zip(funcs, vals).map { $0($1) }
}

This applies a series of functions that take T to values of type T. We could use that even if T happens to (), but we'd have to generate a bunch of () values to make that work. For example:

func gen<T>(funcs: [() -> T]) -> [T] {
return zipMap(funcs, vals: Array(count: funcs.count, repeatedValue: ()))
}

I wouldn't expect this to come up very often in Swift because Swift is mostly an imperative language and hides its Void in almost all cases. But you really do see things like this show up in functional languages like Scala when they bridge over into imperative programming.

How to remove duplicate from tuple

If you use a model value like a struct instead of a tuple

struct TagAndLocation: Hashable {

let tag: String
let location: String

var hashValue: Int { return tag.hashValue }
}

func ==(left:TagAndLocation, right: TagAndLocation) -> Bool {
return left.tag == right.tag && left.location == right.location
}

You can leverage the Set functionalities to remove duplicates

let results: [TagAndLocation] = ...
let uniqueResults = Array(Set(results))

Please note that in this case you lose the original sort order.

How do I add a tuple to a Swift Array?

Since this is still the top answer on google for adding tuples to an array, its worth noting that things have changed slightly in the latest release. namely:

when declaring/instantiating arrays; the type is now nested within the braces:

var stuff:[(name: String, value: Int)] = []

the compound assignment operator, +=, is now used for concatenating arrays; if adding a single item, it needs to be nested in an array:

stuff += [(name: "test 1", value: 1)]

it also worth noting that when using append() on an array containing named tuples, you can provide each property of the tuple you're adding as an argument to append():

stuff.append((name: "test 2", value: 2))


Related Topics



Leave a reply



Submit