1 00:00:00,500 --> 00:00:05,300 Before we start this topic, I do want to stress that the next few videos are quite 2 00:00:05,300 --> 00:00:09,390 a bit more complicated conceptually than what we've learned so far. 3 00:00:09,390 --> 00:00:12,040 We're going to take a look at the intersection of generics and 4 00:00:12,040 --> 00:00:16,400 associated types and their respective characteristics, so fair warning. 5 00:00:16,400 --> 00:00:21,329 However, it's also important to know that you won't necessarily run into code like 6 00:00:21,329 --> 00:00:24,347 this anytime soon and certainly not in our lessons. 7 00:00:24,347 --> 00:00:26,951 But since we're learning about these concepts, 8 00:00:26,951 --> 00:00:29,600 it only makes sense to cover our bases. 9 00:00:29,600 --> 00:00:33,200 Let's go back to associated types in protocols for just a second. 10 00:00:33,200 --> 00:00:34,834 Just like generic classes, 11 00:00:34,834 --> 00:00:39,273 instructs are different from regular classes instructs in how they behave. 12 00:00:39,273 --> 00:00:43,767 A protocol with an associated type has different characteristics compared to 13 00:00:43,767 --> 00:00:45,450 a normal protocol. 14 00:00:45,450 --> 00:00:49,680 I'm going to pay some code in here so I can quickly highlight this example. 15 00:00:49,680 --> 00:00:53,930 And you can grab this code from the link in the teacher's notes as always. 16 00:00:53,930 --> 00:00:58,700 Here, we have a protocol, animal, up at the top that contains an associated 17 00:00:58,700 --> 00:01:04,640 type food and a function eat, that defines what an animal eats. 18 00:01:04,640 --> 00:01:09,230 Since this is an associated type, we defer the actual type 19 00:01:09,230 --> 00:01:13,300 that we accept in as an argument to eat to the point of conformance. 20 00:01:14,420 --> 00:01:17,243 Below this, we've defined two kinds of food. 21 00:01:17,243 --> 00:01:19,730 We have Kibble and DogFood. 22 00:01:19,730 --> 00:01:22,673 And we've created two classes, Cat and Dog, 23 00:01:22,673 --> 00:01:27,597 that conform to animal and substitute in the correct food for the eat method. 24 00:01:27,597 --> 00:01:32,250 So Cats eat Kibble, Dogs eat DogFood, so far, so good. 25 00:01:32,250 --> 00:01:36,320 We've also created an instance of each class down here at the bottom, and 26 00:01:36,320 --> 00:01:38,740 you'll see that all this code so far more or 27 00:01:38,740 --> 00:01:43,000 less works like any protocol code that we've written in the past. 28 00:01:43,000 --> 00:01:47,980 But we've talked before about how types that conform to a protocol 29 00:01:47,980 --> 00:01:52,130 can be represented to the compiler as the protocol type itself. 30 00:01:52,130 --> 00:01:52,720 For example, 31 00:01:52,720 --> 00:01:58,310 we can say that this instance of cat down here is actually of type animal. 32 00:01:58,310 --> 00:02:03,740 But if we try and do that in this case, you'll see that we get an error. 33 00:02:03,740 --> 00:02:06,490 With protocols that have associated types, 34 00:02:06,490 --> 00:02:11,360 we can't use them as variable types or as parameter types in functions. 35 00:02:11,360 --> 00:02:14,710 This means that if we wanted to group various objects together that have 36 00:02:14,710 --> 00:02:16,230 a common protocol. 37 00:02:16,230 --> 00:02:19,670 For example, if we wanted to create an array with this instance of cat and 38 00:02:19,670 --> 00:02:20,950 dog together. 39 00:02:20,950 --> 00:02:25,590 And set that array type as an array of animal, we won't be able to do that. 40 00:02:25,590 --> 00:02:30,510 The only way we could achieve this is to cast each type to an array of any. 41 00:02:30,510 --> 00:02:34,450 And in doing so, we lose all information about each specific type. 42 00:02:34,450 --> 00:02:38,938 Even though we learned about generics and associated types in the same context, 43 00:02:38,938 --> 00:02:42,964 there's actually a subtle difference in what they mean while both allow 44 00:02:42,964 --> 00:02:44,747 you to defer specifying a type. 45 00:02:44,747 --> 00:02:48,282 A generic class or struct arena defers that's 46 00:02:48,282 --> 00:02:53,000 type specification to the point of instantiation. 47 00:02:53,000 --> 00:02:55,600 Until you actually create an instance of the class, 48 00:02:55,600 --> 00:02:58,420 you don't have to specify the type. 49 00:02:58,420 --> 00:03:02,960 On the other hand, with protocols and associated types, you defer it to 50 00:03:02,960 --> 00:03:08,630 the point of protocol conformance, which is when you define the body of the class. 51 00:03:08,630 --> 00:03:13,860 So here in the cat class once cat specifies, that kibble is the food that 52 00:03:13,860 --> 00:03:18,810 it eats and that this is the type where substituting for the food associated type. 53 00:03:18,810 --> 00:03:22,970 This is always going to be a fixed type for every cat instance. 54 00:03:22,970 --> 00:03:27,200 You can change the food that this cat eats depending on the instance you create. 55 00:03:27,200 --> 00:03:31,950 When we try to annotate cat down here, to specify the type as animal, 56 00:03:31,950 --> 00:03:37,300 the error we got said, protocol animal can only be used as a generic constraint, 57 00:03:37,300 --> 00:03:41,340 because it has self or associated type requirements. 58 00:03:41,340 --> 00:03:42,460 What does this mean? 59 00:03:42,460 --> 00:03:47,420 Well, with generic types, type parameters are part of the public interface. 60 00:03:47,420 --> 00:03:50,560 You specified when you create an instance. 61 00:03:50,560 --> 00:03:54,180 With associated types, this information is hidden in the class internals. 62 00:03:54,180 --> 00:03:59,010 And the reason you can't group them as a protocol type, is because different 63 00:03:59,010 --> 00:04:03,950 classes that conform to the protocol have different implementations. 64 00:04:03,950 --> 00:04:08,980 If we had an array of cat and dog here, we can't iterate through that array and 65 00:04:08,980 --> 00:04:12,170 call the eat method, because that function except 66 00:04:12,170 --> 00:04:16,750 a different argument depending on the instance, and its implementation differs. 67 00:04:16,750 --> 00:04:19,280 So the main takeaway is that if you start 68 00:04:19,280 --> 00:04:24,400 using protocols with associated types in your code, you need to be aware of this. 69 00:04:24,400 --> 00:04:28,570 What if you did want to achieve something along these lines, however? 70 00:04:28,570 --> 00:04:32,350 Let's say we had a feed function, and as an argument, 71 00:04:32,350 --> 00:04:35,980 we wanted to accept an instance of an animal to feed them. 72 00:04:35,980 --> 00:04:39,980 Well, we know now that we can't do this directly because we can't use 73 00:04:39,980 --> 00:04:45,090 a protocol that has an associated type as a variable or a parameter type. 74 00:04:45,090 --> 00:04:49,250 For example, if I say func feed(_ animal: and 75 00:04:49,250 --> 00:04:52,780 then I specify the type as animal, I just can't do that. 76 00:04:52,780 --> 00:04:54,350 This is not going to work. 77 00:04:54,350 --> 00:04:55,180 What we can do, 78 00:04:55,180 --> 00:05:00,570 however, is combine our knowledge of generics along with associated types. 79 00:05:00,570 --> 00:05:05,581 So here, I can make this feed function a generic function, 80 00:05:05,581 --> 00:05:10,605 by introducing a single type parameter, we'll call it T. 81 00:05:10,605 --> 00:05:14,700 And we'll say that T must conform to the animal protocol. 82 00:05:14,700 --> 00:05:18,870 And now we can accept an instance of T rather than animal. 83 00:05:18,870 --> 00:05:22,530 And what we're really doing is accepting an instance of an object conforming 84 00:05:22,530 --> 00:05:25,550 to animal, as an argument for a function. 85 00:05:25,550 --> 00:05:31,270 This is the only way we can use a protocol with an associated type as an argument, 86 00:05:31,270 --> 00:05:32,570 as a generic one. 87 00:05:32,570 --> 00:05:37,420 In fact, we can use it this way in classes or as structs too, or even enums. 88 00:05:37,420 --> 00:05:39,010 For example, with cat and 89 00:05:39,010 --> 00:05:43,980 dog, we couldn't defer the choice of what they ate to when we created an instance. 90 00:05:43,980 --> 00:05:45,680 A cat is always going to eat kibble. 91 00:05:45,680 --> 00:05:47,530 A dog is always going to eat dog food, 92 00:05:47,530 --> 00:05:52,200 but if we combine associated types of genetics, we can do that. 93 00:05:52,200 --> 00:05:55,960 Let's declare a new class, we'll call it wolf. 94 00:05:55,960 --> 00:05:58,930 And we'll give it a single type parameter, we'll call it food type. 95 00:06:01,020 --> 00:06:04,600 Wolf is also going to conform to the animal protocol. 96 00:06:05,620 --> 00:06:10,375 Now in the implementation of animals requirements, which is the eat method. 97 00:06:10,375 --> 00:06:15,005 We can specify that the food that we accept is the generic type, FoodType. 98 00:06:16,265 --> 00:06:19,855 This way, when we go ahead and create an instance of wolf, so 99 00:06:19,855 --> 00:06:24,502 say, let wolf, we can specify a type for the type parameter, thereby, 100 00:06:24,502 --> 00:06:28,272 varying the foods different instances of a wolf eats. 101 00:06:28,272 --> 00:06:33,562 So this first wolf can eat some kibble, and then another wolf 102 00:06:33,562 --> 00:06:38,970 can come along, And eat some dog food. 103 00:06:41,050 --> 00:06:45,480 But if you've been following along carefully, you'll notice a problem here. 104 00:06:45,480 --> 00:06:47,491 If you haven't, let me create another instance of wolf. 105 00:06:47,491 --> 00:06:51,710 So I'll say, let thirdWolf = Wolf. 106 00:06:51,710 --> 00:06:52,520 And guess what? 107 00:06:52,520 --> 00:06:55,230 We're going to feed this Wolf integers. 108 00:06:55,230 --> 00:06:57,850 How amazing this Wolf can eat numbers. 109 00:06:57,850 --> 00:07:02,100 Obviously, the issue here is that our generic parameter isn't constrained, so 110 00:07:02,100 --> 00:07:05,890 we can pass anything in, and this satisfies the requirements. 111 00:07:05,890 --> 00:07:09,950 Now, one way we can go about this is to maybe create a base class for 112 00:07:09,950 --> 00:07:12,500 food or another protocol. 113 00:07:12,500 --> 00:07:13,850 Let's go to protocol route. 114 00:07:13,850 --> 00:07:19,630 So up at the top, above Animal, let's create a new protocol. 115 00:07:19,630 --> 00:07:21,700 We'll call this animal food. 116 00:07:21,700 --> 00:07:22,907 And for the sake of this example, 117 00:07:22,907 --> 00:07:26,880 we'll just assume that the protocol encode some information, and we'll keep it empty. 118 00:07:26,880 --> 00:07:30,750 Just like we can add type constraints to a type parameter, 119 00:07:30,750 --> 00:07:34,370 we can add constraints to an associated type as well. 120 00:07:34,370 --> 00:07:38,900 So back inside the animal protocol, we can constrain the associated type food 121 00:07:38,900 --> 00:07:43,770 to say that whatever we substitute must conform to animal food. 122 00:07:43,770 --> 00:07:45,535 When we do that, we're going to get a bunch of errors. 123 00:07:45,535 --> 00:07:47,499 And to satisfy this, first, 124 00:07:47,499 --> 00:07:51,880 we need to make sure that each of our foods conform to animal food. 125 00:07:51,880 --> 00:07:56,990 So kibble conforms to animal food, and dog conforms to animal food. 126 00:07:56,990 --> 00:07:59,810 This way, it ensures that we only pass in foods. 127 00:07:59,810 --> 00:08:02,300 So now, all this code should be resolved because kibble, 128 00:08:02,300 --> 00:08:07,100 in fact, is a type that we're substituting for food that conforms to animal food, and 129 00:08:07,100 --> 00:08:08,160 the same goes for dog food. 130 00:08:09,210 --> 00:08:11,950 Now, this cleared up some errors, but not all. 131 00:08:11,950 --> 00:08:15,158 Our generic type, Wolf, still has an error. 132 00:08:15,158 --> 00:08:19,260 And that's because our generic type parameter that we've defined here that 133 00:08:19,260 --> 00:08:21,154 eventually will be substituted for 134 00:08:21,154 --> 00:08:25,080 food violates the contract imposed by the animal protocol. 135 00:08:25,080 --> 00:08:29,040 Food type, the generic type parameter, has no constraints. 136 00:08:29,040 --> 00:08:34,820 And we're not ensuring that the argument to eat here will conform to animal food. 137 00:08:34,820 --> 00:08:38,310 So, of course, over here, we need to introduce a type constraint as well. 138 00:08:38,310 --> 00:08:41,470 And we'll say that food type needs to conform to animal food. 139 00:08:41,470 --> 00:08:46,311 Doing this, raises an error in the line of code where we try to specify an integer as 140 00:08:46,311 --> 00:08:49,500 a food type which is exactly what we want. 141 00:08:49,500 --> 00:08:55,410 Now, you might be wondering why if we introduced a protocol called animal food, 142 00:08:55,410 --> 00:08:59,680 did we need to also have an associated type up here? 143 00:08:59,680 --> 00:09:02,190 For example, if we go back to the animal protocol and 144 00:09:02,190 --> 00:09:05,360 we comment this line of code out. 145 00:09:05,360 --> 00:09:09,060 And instead, change the argument type to be animal food, 146 00:09:09,060 --> 00:09:11,070 doesn't this solve our problem? 147 00:09:11,070 --> 00:09:13,796 Well, when we do that, again, we're going to have a bunch of errors. 148 00:09:13,796 --> 00:09:18,508 And that's because now these, we have these concrete types specified as 149 00:09:18,508 --> 00:09:23,680 the arguments to the eat method where the compiler is expecting a protocol type. 150 00:09:24,720 --> 00:09:29,403 If we go ahead and change those, so I'll say, animal food here, 151 00:09:29,403 --> 00:09:33,070 and animal food here as well. 152 00:09:33,070 --> 00:09:36,320 And for now, we'll just comment out the wolf code, so 153 00:09:36,320 --> 00:09:37,860 as to not have to deal with that. 154 00:09:39,170 --> 00:09:43,705 Our code compiles, and you can see here, that I can say cat.eat. 155 00:09:43,705 --> 00:09:46,776 And I can pass in an instance of kibble, and it works, 156 00:09:46,776 --> 00:09:49,780 which is the original goal, right? 157 00:09:49,780 --> 00:09:51,220 So, what's the problem here? 158 00:09:51,220 --> 00:09:52,959 The problem X actually quite subtle. 159 00:09:52,959 --> 00:09:57,782 The problem is that because the argument type now is the higher 160 00:09:57,782 --> 00:10:02,511 protocol type AnimalFood, we can also feed our cat anything 161 00:10:02,511 --> 00:10:07,540 that conforms to AnimalFood, including cat.eat, DogFood. 162 00:10:08,540 --> 00:10:11,239 My cat actually eats DogFood, so this wouldn't be an issue. 163 00:10:11,239 --> 00:10:15,140 But you can see that we've lost some specificity here. 164 00:10:15,140 --> 00:10:18,230 I could feed my cat, bird food, for example, and 165 00:10:18,230 --> 00:10:21,380 the compiler wouldn't complain, but my cat would. 166 00:10:21,380 --> 00:10:25,780 So again, here, by specifying the protocol as the argument type, 167 00:10:25,780 --> 00:10:29,220 we've restricted arguments to be instances conforming to animal food, 168 00:10:29,220 --> 00:10:30,842 but that's not specific enough. 169 00:10:30,842 --> 00:10:36,330 I'm gonna undo all the changes here, And 170 00:10:36,330 --> 00:10:41,040 go back up to, there we go. 171 00:10:41,040 --> 00:10:42,340 Back to the way we were. 172 00:10:42,340 --> 00:10:46,660 On the other hand, when we use the associated type, this meant that when we 173 00:10:46,660 --> 00:10:51,613 declared the class, we needed to specify as an argument a class or 174 00:10:51,613 --> 00:10:54,690 a struct that conformed to animal food. 175 00:10:54,690 --> 00:10:56,250 But whatever we specified, 176 00:10:56,250 --> 00:11:00,690 that concrete type was then the only type that would be expected. 177 00:11:00,690 --> 00:11:02,880 So using the associated type, 178 00:11:02,880 --> 00:11:07,120 we're still guaranteeing that the cat eats food that conforms to animal food, but 179 00:11:07,120 --> 00:11:11,760 it's only the type that we say is acceptable for cats to eat. 180 00:11:11,760 --> 00:11:14,790 We get more control in how we restrict our types, but 181 00:11:14,790 --> 00:11:19,690 at the same time, allowing different types to implement the same method differently. 182 00:11:19,690 --> 00:11:24,140 This works the same way if animal food were a base class rather than a protocol. 183 00:11:24,140 --> 00:11:27,090 It's important to note that the constraints you can add to associated 184 00:11:27,090 --> 00:11:29,120 types, as we've done here, 185 00:11:29,120 --> 00:11:33,080 are not as flexible as with the constraints we can add to genetics. 186 00:11:33,080 --> 00:11:35,720 But that is only a problem for right now. 187 00:11:35,720 --> 00:11:40,660 Right now, I'm using Swift 3, excepted for the implementation in Swift 4 188 00:11:40,660 --> 00:11:44,950 is the ability to add a where clause to an associated type. 189 00:11:44,950 --> 00:11:47,740 This introduces far more power and capability, 190 00:11:47,740 --> 00:11:50,830 but we'll tackle that probably as an additional video 191 00:11:50,830 --> 00:11:55,280 once that feature is out in Swift 4, which is probably late 2017. 192 00:11:55,280 --> 00:11:57,130 Okay, let's take another break. 193 00:11:57,130 --> 00:12:00,390 We still have some more digging to do regarding associated types, 194 00:12:00,390 --> 00:12:01,840 and we'll do that in the next video.