Talkin’ About My Generics — Part Two (Swift 3)

Erica Millado
Yay It’s Erica
Published in
5 min readApr 1, 2017

“Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters.” — Wikipedia

Writing code with generics is a way of writing functions and data types without specifying what exact type needs to be used. Like the name suggests, generic types are not specific.

By using generics, we can write code that isn’t specific and therefore, we can abstract our code in a sophisticated manner that is cleaner and less buggy.

In my last post, I described how arrays, dictionaries and optionals are examples of generics. In this Part Two post, I will discuss Writing Generic Data Classes and Structures, Writing Generic Functions and Constraining Generic Types. Part Three will discuss Associated Types and Generic Where Clauses.

Writing Generic Data Classes and Structures

While arrays, dictionaries and optionals are all examples of generics, we can also use generics to make our own data types. For example, I can make a stack data type with generics — not specific to any type of data (floats, doubles, ints). A stack uses a LIFO (last in, first out) process of PUSHING a new item on top of the stack and then POPPING the newest item off the stack. I like to think of it like a stack of cards, placing them one on top of another, and only being able to remove from the top.

Let’s say that I want to make my own stack. I can use angle brackets to specify that it will be generic, meaning, I can decide WHAT I want to stack when I create the stack. In this case, I want to make a stack of strings that represent all the coding books I am currently reading.

What coding books are you currently reading?

1: I create a struct to represent my EricasStack data type. Note that I use the word “Generic” between the angle brackets. I could have written any word there (“tacos” maybe?) that would serve as a placeholder for the value I use for this EricasStack.

2: I create an array of Generics that will hold my stack of coding books.

3: I want to add two books at a time to my stack, so I write a function that PUSHES two items.

4: I want to remove two books at a time to my stack, so I write a function that returns two items to be POPPED.

5: I create an instance of my EricasStack with a <String> type. Note that I can only append strings now. I push two books onto the CodingBooksStack, then I push two more books (four books total).

I then printed out what my final CodingBooksStack contains.

You can also see a print out of what gets POPPED off when I call .popTwoItems( ).

I could also make an EricasStack of money:

Line #38: Stacks on deck!

As you can see above, the < > can be filled with any type when I create an instance of my EricasStack class.

Writing Generic Functions

Let’s say I wanted to take an Int and then duplicate it in an array.

Let’s say I wanted to take a String and then duplicate it in an array.

Let’s say I wanted to take a Float and then duplicate it in an array.

Should I write three different functions for these three different types? I can use GENERICS to write just one function that will work for all types.

Functions and methods can be generic in the context of a generic type.

Line #19: I make taco copies. Line #22: I make pager copies. Remember when pagers were all the rage?

1: I write a function with a generic type named <ItemToDuplicate> that takes in an item and an integer that specifies how many times to copy the ItemToDuplicate.

2: I create an empty array to hold my arrayOfDuplicates.

3: I loop the numberOfTimes and each team I add a ItemToDuplicate to my arrayOfDuplicates array.

4: I return the filled arrayOfDuplicates.

5: I print out a few examples of how this works with different types of strings as the ItemToDuplicate.

Note that I could have also used Ints, Doubles, Floats instead of strings.

Constraining Generic Types

In the above example, any data type (Int, Float, String) could be used in the generic duplicateAndMakeArray function. I could get more specific and specify what TYPE of data type I could take within this generic context.

This process can be done with some protocols that are part of the Swift standard library. Some examples of generic constraining protocols include hashable (i.e. must be able to make itself represented uniquely (ahem, dictionaries are generics constrained to the hashable protocol)), comparable (i.e. must have an order like numbers), and equatable (i.e. must be able to be compared to be equal).

Let’s say I want to write a generic function that finds the smallest value in an array. If I am trying to find the SMALLEST value, that means that I need to conform to the Comparable protocol I mentioned earlier.

1: I declare a function that with generic <Type> that conforms to the Comparable protocol. Note that the protocol, as usual, is indicated after a “:”. I take in an array of the generic type and return exactly one optional value of the type (which will be the smallest value in the array).

2: I check to make sure that the array isn’t empty. If it is, I return nil.

3: I take the first item in the inputted array and identify it as the “itemToCompare.”

4: I look through the entire array and as I loop, I compare the first item to each subsequent item.

5 and 6: If my itemToCompare is larger than each subsequent item, then I reassign the itemToCompare to be that larger item.

7: If not, I return the itemToCompare.

8: I call the findSmallestInArray function with Ints.

9: I call the findSmallestInArray function with Doubles.

This example is just one way that you can use protocols to constrain generic types for written generic functions.

Generics can be used to create data types, write functions and can even be constrained to limit certain types used in these functions.

In part three of my generics posts, I will discuss Associated Types and Where Clauses.

Resources:

Apple Documentation — Generics

--

--