I'd like to write an extension for tuples of (e.g.) two value in Swift. For instance, I'd like to write this swap method:
let t = (1, "one")
let s = t.swap
such that s would be of type (String, Int) with value ("one", 1). (I know I can very easily implement a swap(t) function instead, but that's not what I'm interested in.)
Can I do this? I cannot seem to write the proper type name in the extension declaration.
Additionally, and I suppose the answer is the same, can I make a 2-tuple adopt a given protocol?
You cannot extend tuple types in Swift.
According to
Types, there are named types (which
can be extended) and compound types. Tuples and functions are compound
types.
See also (emphasis added):
Extensions
Extensions add new functionality to an existing
class, structure, or enumeration type.
As the answer above states, you cannot extend tuples in Swift. However, rather than just give you a no, what you can do is box the tuple inside a class, struct or enum and extend that.
struct TupleStruct {
var value: (Int, Int)
}
extension TupleStruct : Hashable {
var hashValue: Int {
return hash()
}
func hash() -> Int {
var hash = 23
hash = hash &* 31 &+ value.0
return hash &* 31 &+ value.1
}
}
func ==(lhs: TupleStruct, rhs: TupleStruct) -> Bool {
return lhs.value == rhs.value
}
As a side note, in Swift 2.2, tuples with up to 6 members are now Equatable.
Details
Xcode 11.2.1 (11B500), Swift 5.1
Solution
struct Tuple<T> {
let original: T
private let array: [Mirror.Child]
init(_ value: T) {
self.original = value
array = Array(Mirror(reflecting: original).children)
}
func getAllValues() -> [Any] { array.compactMap { $0.value } }
func swap() -> (Any?, Any?)? {
if array.count == 2 { return (array[1].value, array[0].value) }
return nil
}
}
Usage
let x = (1, "one")
let tuple = Tuple(x)
print(x) // (1, "one")
print(tuple.swap()) // Optional((Optional("one"), Optional(1)))
if let value = tuple.swap() as? (String, Int) {
print("\(value) | \(type(of: value))") // ("one", 1) | (String, Int)
}
If you wanted to be a Bad Person™ you can define custom operators on tuples, like this:
postfix operator <->
postfix func <-> <A, B>(lhs: (A, B)) -> (B, A) {
return (lhs.1, lhs.0)
}
let initial = (1, "one")
let reversed = initial<->
FWIW I can't think of a place where my 'clever' code trumps the readability of just writing your swap function.
Related
AnyCollection([1, 2, 3]) // AnyCollection<Int>
AnyCollection(["a", "b", "c"]) // AnyCollection<String>
These two AnyCollection is great, but they have different Generic types, namely Int and String.
But I can still add these two AnyCollection to a single array, like this
// [AnyCollection<Any>]
let array = [AnyCollection([1, 2, 3]), AnyCollection(["a", "b", "c"])]
I can't understand what happened to these two AnyCollection.
Why can it convert from String or Int to Any ??
I wrote some code to create one myself.
// ❌ Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
// Contains AnyMyCollection<String> && AnyMyCollection<Int> at a single collection.
var collections = [AnyMyCollection(Sports()), AnyMyCollection(Animals()), AnyMyCollection(Digits())]
protocol MyCollection<Element> {
associatedtype Element
func allValues() -> [Element]
}
// MARK: - AnyMyCollection
struct AnyMyCollection<Element> {
internal var _box: _AnyMyCollectionBase<Element>
init<C: MyCollection>(_ base: C) where C.Element == Element {
self._box = _MyCollectionBox(base)
}
}
extension AnyMyCollection: MyCollection {
func allValues() -> [Element] {
_box.allValues()
}
}
final class _MyCollectionBox<Base: MyCollection>: _AnyMyCollectionBase<Base.Element> {
init(_ base: Base) {
_base = base
}
private var _base: Base
override func allValues() -> [Base.Element] {
_base.allValues()
}
}
class _AnyMyCollectionBase<Element>: MyCollection {
func allValues() -> [Element] {
return []
}
}
// MARK: - Diffrent Types of My Collection
struct Animals: MyCollection {
typealias Element = String
func allValues() -> [Element] {
["Monkey", "Tiger", "Lion"]
}
}
struct Sports: MyCollection {
typealias Element = String
func allValues() -> [Element] {
["Basketball", "Football", "Baseball"]
}
}
struct Digits: MyCollection {
typealias Element = Int
func allValues() -> [Element] {
[1, 2, 3, 4, 5]
}
}
I tried to follow the same technique but failed because the type of the element in AnyMyCollection is not the same.
The simple explanation is that you are not Apple.
Generics in general are not covariant over the parameterized type, and you have no way to create to generic that is covariant over the paramterized type. But Apple does.
To see this more simply, consider Array. It is a generic. If you have two classes, a class and its subclass, you can combine arrays of each of them:
class MyClass {}
class MySubclass: MyClass {}
let arr1 = [MyClass()]
let arr2 = [MySubclass()]
let arr3 = [arr1, arr2] // [[MyClass]]
The compiler accepts this; it treats the resulting combination as an array of arrays of the superclass, Array<Array<MyClass>>. So for Apple, an Array<MySubclass> is treated as a sort of subclass of Array<MyClass>.
But now try to do that with your own generic. You can't do it:
class MyGeneric<T> {}
let g1 = MyGeneric<MyClass>()
let g2 = MyGeneric<MySubclass>()
let arr4 = [g1, g2] // error
The compiler does not magically see this as an Array<MyGeneric<MyClass>>. That's because your generic is not covariant.
There are always language proposals sitting around, hoping to allow us ordinary human beings to make covariant generics. But so far, none of them has arrived into the language.
I have an array of functions like
let array = [(Int) -> T?, (Int) -> T?, (Int) -> T?,...]
I need to get first non nil T value from array and I want 1 iteration for this task (O(n) will be the worst complexity). Anybody has neat ideas without for loops?
As I mentioned in my comment, there's no ready-made way to do this in Swift, so you have to at least implement something that uses a for loop.
If you want appearances at the call-site to look functional and not use a for loop, you can make the function extend Array like so:
extension Array {
func firstNonNilResult<V, T>(value: V) -> T? where Element == (V) -> T? {
for element in self {
if let t = element(value) {
return t
}
}
return nil
}
}
var array = [(Int) -> String?]()
func f(_ i: Int) -> String? {
print("called f() with \(i)")
return nil
}
func g(_ i: Int) -> String? {
print("called g() with \(i)")
return i == 5 ? "found it" : nil
}
array.append(f)
array.append(g)
if let t = array.firstNonNilResult(value: 5) {
print(t)
}
which prints:
called f() with 5
called g() with 5
found it
Whether or not this has any real-world utility I can't say. I think it's specialized enough that an Array extension seems like overkill, but your question is interesting so this is at least a potential solution.
Is there a more elegant way to filter with an additional parameter (or map, reduce).
When I filter with a single parameter, we get a beautiful easy to ready syntax
let numbers = Array(1...10)
func isGreaterThan5(number:Int) -> Bool {
return number > 5
}
numbers.filter(isGreaterThan5)
However, if I need to pass an additional parameter to my function it turns out ugly
func isGreaterThanX(number:Int,x:Int) -> Bool {
return number > x
}
numbers.filter { (number) -> Bool in
isGreaterThanX(number: number, x: 8)
}
I would like to use something like
numbers.filter(isGreaterThanX(number: $0, x: 3))
but this gives a compile error annonymous closure argument not contained in a closure
You could change your function to return a closure which serves
as predicate for the filter method:
func isGreaterThan(_ lowerBound: Int) -> (Int) -> Bool {
return { $0 > lowerBound }
}
let filtered = numbers.filter(isGreaterThan(5))
isGreaterThan is a function taking an Int argument and returning
a closure of type (Int) -> Bool. The returned closure "captures"
the value of the given lower bound.
If you make the function generic then it can be used with
other comparable types as well:
func isGreaterThan<T: Comparable>(_ lowerBound: T) -> (T) -> Bool {
return { $0 > lowerBound }
}
print(["D", "C", "B", "A"].filter(isGreaterThan("B")))
In this particular case however, a literal closure is also easy to read:
let filtered = numbers.filter( { $0 > 5 })
And just for the sake of completeness: Using the fact that
Instance Methods are Curried Functions in Swift, this would work as well:
extension Comparable {
func greaterThanFilter(value: Self) -> Bool {
return value > self
}
}
let filtered = numbers.filter(5.greaterThanFilter)
but the "reversed logic" might be confusing.
Remark: In earlier Swift versions you could use a curried function
syntax:
func isGreaterThan(lowerBound: Int)(value: Int) -> Bool {
return value > lowerBound
}
but this feature has been removed in Swift 3.
Working with Swift generics, I have the following question:
This function works as expected with the type Int:
func + (number: Int, vector: [Int]) -> [Int] {
var resArray:[Int]=[]
for x:Int in vector {
resArray.append(number+x)
}
return resArray
}
I want to make it work with any type where addition makes sense.
I have tried the following:
func +<T:NSNumber> (number: T.Type, vector: [T.Type]) -> [T.Type] {
var resArray:[T.Type]=[]
for x:T.Type in vector {
resArray.append(number+x)
}
return resArray
}
But the line:
resArray.append(number+x)
hits a problem because number and x should obvious support addition.
How should I change my code? I suppose I need to add a constraint on the type. I don't quite know how.
You can define such a constraint as you said, as a protocol like AdditiveSemigroup below.
protocol AdditiveSemigroup {
typealias Out = Self
static func + (a: Self, b: Self) -> Out
}
func +<T: AdditiveSemigroup where T.Out == T> (value: T, vector: [T]) -> [T] {
return vector.map { $0 + value }
}
To make a type conform the protocol above, just define extension on that type.
extension String: AdditiveSemigroup {}
"A" + ["A", "B", "C"] // ==> ["AA", "AB", "AC"]
For the NSNumber, there've been no built-in + operatator, so you have to define it by hand.
extension NSNumber: AdditiveSemigroup {
typealias This = NSNumber
}
func + (a: NSNumber, b: NSNumber) -> NSNumber {
return NSNumber(double: a.doubleValue + b.doubleValue)
}
Now you could apply your special + operator to values of NSNumber.
NSNumber(double: 3) + [NSNumber(double: 5)] // ==> 8
As an exercise, I'm implementing a map function that takes an array and a function and applies the function to all elements of the array, but I don't know how to declare it such that it works for any type of array.
I can do something like
func intMap(var arr: [Int], fun: (Int) -> Int) -> [Int] {
for i in 0 ..< arr.count {
arr[i] = fun(arr[i])
}
return arr
}
intMap([1,2,3], {x in return x * x})
But this only works for int.
What is the type signature for Swift's built-in map?
Edit:
So I was missing the fact that I can declare param type signatures without declaring their types explicitly.
func myMap<T>(var arr: [T], fun: (T) -> T) -> [T] {
for i in 0 ..< arr.count {
arr[i] = fun(arr[i])
}
return arr
}
myMap([1,2,3], fun: {
x in return x * x
})
Create a new Playground
Just under where it has import UIKit type import Swift
Command click on the word Swift
This will open the Swift library and you can see all the type definitions there.
And you can see:
extension CollectionType {
/// Return an `Array` containing the results of mapping `transform`
/// over `self`.
///
/// - Complexity: O(N).
#warn_unused_result
#rethrows public func map<T>(#noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
Edited to add
Alternatively, you can write a more generalised map
func myMap<T, U>(var arr: [T], fun: T -> U) -> [U] {
var a: [U] = []
for i in 0 ..< arr.count {
a.append(fun(arr[i]))
}
return a
}
Which returns a new array, of a possibly different type, which you can see for yourself by putting this in your playground.
let a = [1, 2, 3]
let b = myMap(a, fun: { x in Double(x) * 2.1 })
a
b