Hi guys, in this post we’ll look at generic programming and functions that you can use when coding in Swift, and how useful they can be. Let’s get right to it.
For those of you not familiar with the concept, generic programming is a style of programming in which algorithms are written in terms of types to-be specified –later, that are instantiated when needed, for specific types provided as parameters. This approach, pioneered by ML (general-purpose functional programming language) in 1970, enables you to write common functions or types that differ only in the set of types on which they operate when used, thereby reducing duplication.
Generics are primarily used avoid duplication and to provide abstraction. Swift also provides certain generic features that allow you to write flexible and reusable functions and types. In fact, Swift’s own standard libraries are built with generics code. For example, Swift’s ‘Array’ and ‘Dictionary’ types belong to generic collections. Any arrays you create using these generic arrays and dictionaries, can be defined to hold ‘Int’values and ‘String’ values, or any other types.
Let’s consider the example of the swapInt(_: _:): function, as shown below.
func swapInt(inout a: Int, inout b: Int) { let temp = a a = b b = temp } var numb1 = 100 var numb2 = 200 print("Before Swapping values are: \(numb1) and \(numb2)") exchange(&numb1, &numb2) print("After Swapping values are: \(numb1) and \(numb2)")
If you run the above code you’ll get the following result:
Before Swapping values are: 100 and 200 After Swapping values are: 200 and 100
As you can see, the swapInt(_:_:) method is particularly useful, as it swaps the original value of b into a, and the original value of a into b. However, it can only be used with Int values. If you want to do more with the swapInt(_:_:) method, like swapping two String values or two Doubles, then you’ll have to write more functions, such as swapString(_:_:) and swapDouble(_:_:)as shown below:
func swapString(inout a: String, inout b: String) { let temp = a a = b b = temp }
func swapDouble(inout a: Double, inout b: Double) { let temp = a a = b b = temp }
As you can see, only the function signatures of swapDouble(_:_:), swapString(_:_:)and swapInt(_:_:) are different, and they share identical bodies. So if you’re using these functions in combination, you not only end up with two functions, you also end up with repeated code inside them. A more useful approach would be to write a single function that can swap two values of any type. That’s where generic functions come in.
Generic functions can work with any type. Given below is the generic version of the swapInt(_:_:) function that we used above, which is called swapValues(_:_:)
func swapValues<T>(inout a: T, inout b: T) { let temp = a a = b b = temp }
As you can see, there is a slight difference in both functions’ signature
func swapValues<T>(inout a: T, inout b: T); func swapInt(inout a: Int, inout b: Int);
In the generic function’s signature, T is used as a placeholder type name instead of the actual type (Int, String or Double etc). The actual Type to use in place of T will be determined each time the swapValues(_:_:) function is called. The other difference is the type parameter.
Note
Type parameters are used specify and name a placeholder type, and are written immediately after the function’s name between a pair of matching angle brackets (such as <T >). There can be more than one type parameter names in single generic function separated by commas.
The swapValues(_:_:) function can now be called in the same way as swapInts(_:_:), but both parameters’ types should be the same. That way each time swapValues(_:_:) is called, the type to use for T is inferred from the types of values passed to the function.
Swift also allows you to define your own generic types. These are custom classes, structures and enumerations that can work with any type, in the same manner as Array and Dictionary.
Lets consider the example of the queue function (as depicted in the snippet below).
A queue is a data structure similar to a list or stack where you can add new values only at the end of the queue (known as en-queuing) and take values values only from from the top of the queue (de-queuing).
struct Queue<Element> { private var elements = [Element]() mutating func enqueue(newElement: Element) { elements.append(newElement) } mutating func dequeue() -> Element? { guard !elements.isEmpty else { return nil } return elements.removeAtIndex(0) } } var q = Queue<Int>() q.enqueue(4) q.enqueue(2) q.dequeue() q.dequeue() q.dequeue() q.dequeue()
In the above code, Queueis a generic type over Element instead of the actual type (Int, String and Double etc.). Element here defines a placeholder name for “some type Element” to be provided later on. Since it is a generic type, you can use the Queue to store any data type. In the above example for instance, we’re using the Queue to store integers (Int).
Lets look at an example of a sorting method. The following method will sort the data and return the middle value after applying sorting:
func mid<T>(array: [T]) -> T { return array.sort()[(array.count - 1) / 2] }
This code is not compliable however. The problem is the sort() method used, which requires an array with elements should be Comparable. By changing function declaration to the snippet show below, will solve the problem:
func mid<T: Comparable>(array: [T]) -> T { return array.sort()[(array.count - 1) / 2] }
What we’ve done above is simply used a type constraint. You write type constraints by placing a single class or protocol constraint after a type parameter’s name, separated by a colon, as a part of the type parameter list.
By adding type constraints, you are imposing a restriction that the data type used in a generic function should implement the type constraints protocol.
In the above post I showed you to how to write reusable methods using generic programming in Swift and how you can create custom types using generics in Swift. Hope you find it useful.
USA408 365 4638
1301 Shoreway Road, Suite 160,
Belmont, CA 94002
Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.
Tel:
+1 408 365 4638
Support:
+1 (408) 512 1812
COMMENTS ()
Tweet