Treehouse Intermediate Course - Part 7 (Generics 5)

The roll continues! So I will do similar to the other week - do several entries within one blog as it may be a bit stop-start. I've got around 20 minutes now just to make a start. By the end of today I will be finished with generics! I will then do a follow up consolidation entry with extra practice before moving on!

Start Time - 12:53

Associated Types

Can protocols be made generic? It is already in the sense that any type can conform to a protocol. But what about the internal? As in the code that goes behind the scenes...

Stack - a bit like an array but more limited. The main point is the order - last in, first out. That makes sense.

Associated type for the function - you can use a placeholder name for the type that is used in the protocol.

The actual type is not specified until we actually adopt the protocol.

Here is the protocol -

protocol Stack {
    
    associatedtype Element
    
    mutating func push(_ element: Element)
    
    mutating func pop() -> Element
    
    var top: Element? { get }
    
    

}

So I haven't done these for a while but protocols are a blueprint for behaviour. They don't have the actual code. That's why the functions don't need code bodies!

Here is the struct which conforms to the protocol stack - 

struct IntStack: Stack {
    
    typealias Element = Int
    
    private var array = Array<Int>()
    
    mutating func push(_ element: Int) {
        array.append(element)
    }
    
    mutating func pop() -> Int? {
        return array.popLast()
    }
    
    var top: Int? {
        return array.last
    }
}

This is giving a protocol generic requirements. 

Challenge on type constraints - 



protocol ConfigurableRow {
 
  associatedtype Object
 
  func configure(with object: Object)
 
}

Nailed it! Gonna do 5 more minutes before going out with my friend Ben for coffee.

Constraints with Where Clauses

What if we have more than one constraint requirement?

Paused at 13:10 (17 minutes so far)

Resumed at 16:15

Ok, I was out for longer than expected but back now! Where were we....

Ah yes, our own struct with hashable... One of the features we need to take is the multiple inheritance....

protocol HashableShoppingItem: Hashable, ShoppingItem { }

struct Checkout<Item: HashableShoppingItem> {
    
    var cart = [Item: Int]()
    

}

So to explain. We had to create a new protocol which conforms to both Hashable and the ShoppingItem Protocol already created. The struct now has an item that has multiple inheritance. But we had to create an empty protocol - a bit protracted surely??

Yes, another feature is protocol composition. You can list as many protocols as you need to instead.

struct Checkout<Item: Hashable & ShoppingItem > {
    
    var cart = [Item: Int]()
    

}

That is better! 

Another key thing is that we cannot COMBINE a class and protocol. Can't do this with type alias. *Need to read up more on type alias. 

Where clause is used here!

class Shape2 {
    
}

struct Animator<T: Shape2> where T: Hashable {
       
}

There was another way of writing this - which had the same effect. Pasan calls it 'syntactic sugar'. By that he means it is 'sweeter' or easier to read. 

Before continuing, I need to read up on type alias...I've seen it before but need to check out the purpose of it again!

Extra Reading - Type Alias

https://www.programiz.com/swift-programming/typealias

Write so it is a way of providing a new name for an existing type. They do not create a new type!

So they can be for built-in types e.g. Int, String; custom types - certain classes/structs/enums, or complex ones - closures!

Ok that actually makes perfect sense! It's basically for readability. Time to continue!

Constraining Associated Types

Ominous start - Pasan says that this is going to get MORE technical and complicated. Uh oh!

Code from Pasan - 

protocol Animal {
    associatedtype Food
    func eat(_ food: Food)
}

struct Kibble {}

class Cat: Animal {
    func eat(_ food: Kibble) {
        // code
    }
}

struct DogFood {}

class Dog: Animal {
    func eat(_ food: DogFood) {
        //
    }
}

let cat = Cat()
let dog = Dog()

Let's just go through this. So there is an Animal protocol. The associatedtype keyword is used - as before, it means that you don't have to subclass - it avoids inheritance. I think. Yes, doing some reading up on this again means that having associated types means a protocol with generics in it basically. 

So the structs kibble and dogFood are empty - they're just the types for the food the class animals will eat. The class of cat and the class of dog each conform to the protocol - the BEHAVIOUR - of animal. The animal must eat basically; that's where the associatedType of food comes in. Then instances have been made.

class Wolf<FoodType>: Animal {
    func eat(_ food: FoodType) {
        
    }
}

let wolf = Wolf<Kibble>()

let wolf2 = Wolf<DogFood>() 

Complicated stuff. 

A general thing I've picked up is that if you have a type conforming to a protocol, then it can lose its specificity. 

Using the associated type meant that you needed a class/struct to conform to whatever the associated type conforms to!

So Swift 4 can use the where clause to associated types. This is already out - Swift 5 actually is - so I'm not going to get bogged down. 

This is SOOOO technical, as Pasan warned! But I don't see the point of obsessing over it!

Going to stop there - that will do for now!

Extra Reading/Practice

https://medium.com/swift-india/power-of-swift-generics-part-1-ab722a030dc2

Just want to clear some things up!

  • OK, as Swift is type safe, we have to specify the type - unless it is clearly inferred of course with the basic, built-in types like Int and String. A key point is that we want to avoid using Any or AnyObject as they lose the specificity of the code! Any means you don't know what custom type or other type it is. 
  • Generics allow you to work with code that matches certain CONSTRAINTS. E.g. array/sets/dictionaries - these are all generics - they have constraints and rules. 
  • For functionality that is GENERIC e.g. I want to print items in an array; it doesn't matter what the type is; I would need to do a specific function per type if needing multiple types to do this for... that's there the <T> is used. The <T> is convention unless a specific name is used. In dictionaries, they have named type parameters: key, element. T is convention as it is for a TYPE not for a value!
  • Type constraints - specify that a type parameter must conform to a particular protocol. Basically, under the hood, there are all sorts of protocols that types must adhere to! So, dictionaries have the hashable protocol; equatable is another protocol that allows you too COMPARE. 

That's helped to clear up generics a little. It's coming together slowly! That was Aaina Jain from Medium.com by the way that I got the content from. 


Finished at 17:10 (45 minutes; 1 hour and 12 minutes total today)


So I've decided against plowing on. I've got time tomorrow and Thursday AND will be back on Sunday! So tomorrow will complete generics before cracking on to closures! I'm most looking forward to the practical courses coming up, which are part of the Intermediate iOS part - distinct from the Intermediate Swift track on Treehouse, though there is some crossover. Anyway, generics are hard and this content is almost indecipherable. I'm going through it because it could be useful but I feel like I need a higher order understanding before this can really click into place. 

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)