Swift testing non-scalar types - swift

I want to test my function that takes a string, a returns all the pairs of characters as an array s.t.
func pairsOfChars(_ s: String) -> [(Character,Character)] {
let strArray = Array(s)
var outputArray = [(Character,Character)]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append( (strArray[i], strArray[j]) )
}
}
return outputArray
}
So I want to create a suite of tests using XCTestCase. I usually use XCTestCase and XCTAssertEqual but these are only appropriate for C scalar types. This means that the following test case returns an error:
class pairsTests: XCTestCase {
func testNaive() {
measure {
XCTAssertEqual( pairsOfChars("abc") , [(Character("a"),Character("b")),(Character("a"),Character("c")),(Character("b"),Character("c")) ] )
}
}
}
I could convert to a string, but I'm thinking there is a better solution.
How can I test an output of an array of pairs of characters [(Character,Character)]

Your notion of a nonscalar is a total red herring. The problem is one of equatability.
How can I test an output of an array of pairs of characters [(Character,Character)]
You can't, because there is no default notion of what it would mean to equate two such arrays. This is the old "tuples of Equatable are not Equatable" problem (https://bugs.swift.org/browse/SR-1222) which still rears its head with arrays. The == operator works on tuples by a kind of magic, but they are still not formally Equatable.
You could define equatability of arrays of character pairs yourself:
typealias CharPair = (Character,Character)
func ==(lhs:[CharPair], rhs:[CharPair]) -> Bool {
if lhs.count != rhs.count {
return false
}
let zipped = zip(lhs,rhs)
return zipped.allSatisfy{$0 == $1}
}
Alternatively, have your pairsOfChars return something that is more easily made equatable, such as an array of a struct for which Equatable is defined.
For example:
struct CharacterPair : Equatable {
let c1:Character
let c2:Character
// in Swift 4.2 this next bit is not needed
static func ==(lhs:CharacterPair, rhs:CharacterPair) -> Bool {
return lhs.c1 == rhs.c1 && lhs.c2 == rhs.c2
}
}
func pairsOfChars(_ s: String) -> [CharacterPair] {
let strArray = Array(s)
var outputArray = [CharacterPair]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append(CharacterPair(c1:strArray[i],c2:strArray[j]))
}
}
return outputArray
}
You would then rewrite the test to match:
XCTAssertEqual(
pairsOfChars("abc"),
[CharacterPair(c1:Character("a"),c2:Character("b")),
CharacterPair(c1:Character("a"),c2:Character("c")),
CharacterPair(c1:Character("b"),c2:Character("c"))]
)

Related

swift generics return first and last element

I'm trying to get used to generics (never used them in objc) and want to write a toy function that takes an object of any type () and returns the first and last element. Hypothetically, I'd only use this on an array or a string - I keep getting an error that has no subscript members. I totally understand that the error message is telling me swift has no clue that T may potentially hold a type that does have subscripts - I just want to know how to get around this.
func firstAndLastFromCollection<T>(a:T?) {
var count: Int = 0
for item in a as! [AnyObject] {
count++
}
if count>1 {
var first = a?[0]
var last = a?[count-1]
return (first, last)
}
return something else here
}
Do I need to typecast somewhere here (which would kind of defeat the purpose here, as I'd need to downcast as either a string or an array, adding code and lessening how generic this func is)?
If you want to return the first and the last element then it's probably safe assuming the input param is an array of some kind of type.
So you can implement your function this way
func firstAndLast<T>(list:[T]) -> (first:T, last:T)? {
guard let first = list.first, last = list.last else { return nil }
return (first, last)
}
The function does return a tuple of 2 element, both have the same type of the generic element of the input array.
The returned tuple is an option because if the array is empty then nil is returned.
Examples
let nums = firstAndLast([1,2,3,4])
let words = firstAndLast(["One", "Two", "Three"])
As you can verify the type of the generic element into the array becomes the type of the elements inside the tuple.
In the example above nums is inferred to be (Int, Int)? and words (Words, Words)?
More examples
let emptyList: [String] = []
firstAndLast(emptyList) // nil
Extension
Finally you can also write this code as an extension of Array.
extension Array {
var firstAndLast: (first:Element, last:Element)? {
guard let first = self.first, last = self.last else { return nil }
return (first, last)
}
}
Now you can write
let aCoupleOfShows = ["Breaking Bad", "Better Call Saul", "Mr Robot"].firstAndLast
Again, if you check the type of the constant aCoupleOfShows you'll see that is a (first: String, last: String)?. Swift automatically did infer the correct type.
Last example
In the comments you said you wanted the first and last chars of a String. here it is the code if you use the extension above
if let chars = Array("Hello world".characters).firstAndLast {
print("First char is \(chars.first), last char is \(chars.last) ")
}
//>> First char is H, last char is d
If we are talking about collections, let's use the CollectionType:
func firstAndLastFromCollection<T: CollectionType>(a: T) -> (T.Generator.Element, T.Generator.Element)? {
guard !a.isEmpty else {
return nil
}
return (a.first!, a.lazy.reverse().first!)
}
print(firstAndLastFromCollection(["a", "b", "c"])) // ("a", "c")
print(firstAndLastFromCollection("abc".characters)) // ("a", "c")
print(firstAndLastFromCollection(0..<200)) // (0, 199)
print(firstAndLastFromCollection([] as [String])) // nil
If you specify your generic type to also conform to bidirectional index:
func firstAndLastFromCollection<T: CollectionType where T.Index : BidirectionalIndexType>(...) -> ...
then you can call last directly:
return (a.first!, a.last!)
If we decide to implement it using a category, we don't need generics at all:
extension CollectionType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.lazy.reverse().first!)
}
}
extension CollectionType where Index: BidirectionalIndexType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.last!)
}
}
print("abc".characters.firstAndLast())
Swift is a protocol oriented language. Usually you will find yourself extend protocols more than extending classes or structs.

Comparing array of force unwrapped optionals

I'm writing a test:
func test_arrayFromShufflingArray() {
var videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
var shuffledVideos = tuple.0
let shuffleIndexMap = tuple.1
// -- test order is different
XCTAssert(videos != shuffledVideos, "test_arrayFromShufflingArray fail")
}
But on the last line I get the last line:
Binary operator '!=' cannot be applied to two '[VMVideo!]' operands
Arrays can be compared with == if the element type is Equatable:
/// Returns true if these arrays contain the same elements.
public func ==<Element : Equatable>(lhs: [Element], rhs: [Element]) -> Bool
But neither ImplicitlyUnwrappedOptional<Wrapped> nor Optional<Wrapped> conform to Equatable, even if the
underlying type Wrapped does.
Possible options are (assuming that VMVideo conforms to Equatable):
Change your code so that videos and shuffledVideos are
[VMVideo] arrays instead of [VMVideo!].
Compare the arrays elementwise:
XCTAssert(videos.count == shuffledVideos.count
&& !zip(videos, shuffledVideos).contains {$0 != $1 })
Define a == operator for arrays of implicitly unwrapped equatable
elements:
func ==<Element : Equatable> (lhs: [Element!], rhs: [Element!]) -> Bool {
return lhs.count == rhs.count && !zip(lhs, rhs).contains {$0 != $1 }
}
Swift can't tell how to compare two arrays to see if their contents are identical unless it knows how to compare individual elements. So you need to implement the == function on your class and adopt Equatable:
extension VMVideo: Equatable {
// nothing goes here, == function has to be at global scope
}
func ==(lhs: VMVideo, rhs: VMVideo) -> Bool {
// Up to you to determine what equality means for your object, e.g.:
return lhs.essentialProperty1 == rhs.essentialProperty1 &&
lhs.essentialProperty2 == rhs.essentialProperty2
}
EDIT To clarify how it interacts with NSObject and to troubleshoot your environment, please confirm the following:
class UnderstandsEqual: NSObject {}
let ok1: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
let ok2: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
ok1 == ok2 // no problem, evaluates to true
class DoesntUnderstand {}
let bad1: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
let bad2: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
bad1 == bad2 // produces a compile-time error
Martin R's answer was the better answer, but for this specific purpose I just converted to NSArray's and was able to use the == operator in Swift:
func test_arrayFromShufflingArray() {
let videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let videosNSArray: NSArray = videos.map { $0 }
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
let shuffledVideos = tuple.0
let shuffledVideosNSArray: NSArray = shuffledVideos.map { $0 }
// -- test order is different
XCTAssert(videosNSArray != shuffledVideosNSArray, "test_arrayFromShufflingArray fail")
// -- test elements are the same
let set = NSSet(array: videos)
let shuffledSet = NSSet(array: shuffledVideos)
XCTAssert(set == shuffledSet, "test_arrayFromShufflingArray fail")
}

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

Index and Iterate over CollectionType in swift

I have code which is basically like this:
func arrayHalvesEqual(data:[UInt8]) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
This works fine, but sometimes I want to pass in Arrays, and other times ArraySlice. I thought I'd change it to use generics and the CollectionType protocol, which converts as follows:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Generator.Element == UInt8>(data:ByteArray) -> Bool {
let midPoint = data.count / 2
for i in 0..<midPoint {
let b = data[i]
let b2 = data[i + midPoint]
if b != b2 {
return false
}
}
return true
}
However, I get the following compiler error:
error: binary operator '..<' cannot be applied to operands of type 'Int' and 'ByteArray.Index.Distance'
for i in 0..<midPoint {
I can switch the for loop to for i in data.indices which makes that compile, but then I can no longer divide it by 2 to get the midPoint, as data.indices returns the abstract CollectionType.Index whereas / 2 is an Int.
Is it possible to do something like this in Swift? Can I bridge between an abstract protocol Index type and some real type I can do maths on?
P.S: I've seen and found other examples for iterating over the whole collection by using indices and enumerate, but I explicitly only want to iterate over half the collection which requires some sort of division by 2
Thanks
You can restrict the method to collections which are indexed
by Int:
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index == Int, ByteArray.Generator.Element == UInt8>
(data:ByteArray) -> Bool { ... }
This covers both Array and ArraySlice.
And if you use indices.startIndex instead of 0 as initial index
then it suffices to restrict the index type to IntegerType.
Also the data type UInt8 can be replaced by a generic Equatable,
and the entire method shortened to
func arrayHalvesEqual<ByteArray : CollectionType where ByteArray.Index : IntegerType, ByteArray.SubSequence.Generator.Element : Equatable>
(data:ByteArray) -> Bool {
let midPoint = (data.indices.endIndex - data.indices.startIndex)/2
let firstHalf = data[data.indices.startIndex ..< midPoint]
let secondHalf = data[midPoint ..< data.indices.endIndex]
return !zip(firstHalf, secondHalf).contains { $0 != $1 }
}

How to compare one value against multiple values - Swift

Let's say that you have the code
if stringValue == "ab" || stringValue == "bc" || stringValue == "cd" {
// do something
}
Is there a way to shorten this condition or beautify it (preferably without using the switch statement)? I know that this code does NOT work:
if stringValue == ("ab" || "bc" || "cd") {
// do something
}
I've seen some complex solutions on other languages, but they seem language specific and not applicable to Swift. Any solutions would be appreciated.
let valuesArray = ["ab","bc","cd"]
valuesArray.contains(str) // -> Bool
You can create an extension like this:
extension Equatable {
func oneOf(other: Self...) -> Bool {
return other.contains(self)
}
}
and use it like this:
if stringValue.oneOf("ab", "bc", "cd") { ... }
Credit for the impl which saved me typing it: https://gist.github.com/daehn/73b6a08b062c81d8c74467c131f78b55/
Not that i am aware, you can do something like this though,
let validStrings = Set<String>(arrayLiteral:"ab", "bc", "cd")
if validStrings.contains(str) {
//do something
}
Use a Switch Statement.
switch stringValue {
case "ab", "bc", "cd":
print("Yay!")
default:
break
}
The construction ["some", "array"].contains("value") works, but is somewhat annoying:
It inverts the left-to-right order you may want to write.
Items in the array are not declared using Swift's type inference, often forcing you to include unnecessary information to please the compiler.
You can instead use Set(["value"]).isSubset(of: ["some", "array"]).
The benefit is especially apparent when working with enums:
enum SomeReallyReallyLongTypeName {
case one, two
}
struct Thing {
let value: SomeReallyReallyLongTypeName
}
let thing = Thing(value: .one)
if Set([thing.value]).isSubset(of: [.one, .two]){
// :)
// Left-to-right order
// You get nice type inference
}
if [SomeReallyReallyLongTypeName.one, .two].contains(thing.value) {
// :(
// Annoying to have "SomeReallyReallyLongTypeName" in the code
}
if someArray.contains(object) {
// contains
} else {
// does not contains
}
The above function returns bool value, then you write logic accordingly.
Just for fun, how about overloading functions over String:
if a.isOneOf("ab", "bc", "cd") {
print("yes")
}
extension String {
#inlinable
func isOneOf(_ first: String, _ second: String) -> Bool {
self == first || self == second
}
#inlinable
func isOneOf(_ first: String, _ second: String, _ third: String) -> Bool {
self == first || isOneOf(second, third)
}
#inlinable
func isOneOf(_ first: String, _ second: String, _ third: String, _ fourth: String) -> Bool {
self == first || isOneOf(second, third, fourth)
}
}
This gives you full performance benefits, as the compiler will be able to inline and tail call as much as it wants, at the cost of having to write as many overloads as you need in your code, and also not being able to pass arrays - but other answers deal with this too.
let a = 1
let b = 1
let c = 1
let d = 1
if a == b,a==c,a==d {
print("all of them are equal")
}
else {
print("not equal")
}