         closure(item: items[index])
     }
 }
-
+/*:
+ - example: Calling the interator functions but passing in dogs and cats and other types.
+ */
 iterator(dogs) { (pet) in
 
     print("\(pet.name) makes a \(pet.makeSound()) sound.")

    print("\(cat.name) makes a \(cat.makeSound()) sound.")
}
/*:
 ## Using Generics with Functions
 
 */
func iterator<T>(items: [T], closure: (item: T) -> Void) {


let dogIsYappy = matt.isYappy(fido)
let catIsNotYappy = matt.isYappy(snowy)
/*:
 - experiment: See if you can pass an `Int` type into `isYappy`
 */

/*:
 ## Associated Types
 When creating a protocol, you can use placeholders, which are just like the parameter type names for generic functions and types, to be used within your protocol. These placeholders are called *associated type* and created with the `associatedtype` keyword. Associated types would be used when you don’t want to be specific about the type that is used in the protocol.
 - example: Below we have a `Kennel` protocol that defines the associated type `Pet`. `Pet` can be anything that makes sense to be a pet, such as an animal, dog or cat. The `BackYardKennel` type conforms to `Kennel` and in do so, uses a `typealias` to alias `Pet` for `Animal` which allows us to implement the rest of the methods in the `Kennel` protocol.
 */
protocol Kennel {

    associatedtype Pet

    func add(pet: Pet)
    
    func remove(pet: Pet) -> Pet?
    
    var count: Int { get }
    
    subscript(i: Int) -> Pet { get }
}

// to make Animal conform to Equatable

func ==(lhs: Animal, rhs: Animal) -> Bool {
    
    return lhs.name == rhs.name
}

// Animal is already conforming to Equatable, make it explicit in the extension

extension Animal: Equatable {}

class BackYardKennel: Kennel {

    var animals = [Animal]()

