1 00:00:06,030 --> 00:00:09,039 In this video, we're going to look at encapsulation, which is the first 2 00:00:09,039 --> 00:00:11,975 important way that we're going to use local. 3 00:00:11,975 --> 00:00:15,559 Encapsulation is one of the most fundamental concepts in software 4 00:00:15,559 --> 00:00:22,540 engineering, and this is what it's about. Imagine a large software system with 10, 5 00:00:22,540 --> 00:00:27,766 20, or 50, or 100 programmers on it. Now, ask yourself, how likely is it in 6 00:00:27,766 --> 00:00:31,862 that situation that two programmers are going to want to use this same function 7 00:00:31,862 --> 00:00:36,040 name for two completely different functions. 8 00:00:37,730 --> 00:00:40,968 It's pretty likely right? It's almost guaranteed to happen, in 9 00:00:40,968 --> 00:00:45,193 fact, and that's the problem that encapsulation solves. 10 00:00:45,193 --> 00:00:49,161 What encapsulation lets us do is take each part of our program and make it into 11 00:00:49,161 --> 00:00:53,315 a capsule, a little package that has a bunch of internal functions and constants 12 00:00:53,315 --> 00:00:58,169 constructors. And only a small number of external 13 00:00:58,169 --> 00:01:02,998 functions in constants and structures. What encapsulation rests on, and I'll 14 00:01:02,998 --> 00:01:08,212 explain why we call it encapsulation as we go, but what encapsulation rests on, 15 00:01:08,212 --> 00:01:13,347 is the observation that with a local, the a and the b in this example don't exist 16 00:01:13,347 --> 00:01:19,924 out here. If we draw the scope contour, that a and 17 00:01:19,924 --> 00:01:24,824 b don't exist out here. Now why would that be useful? 18 00:01:24,824 --> 00:01:30,436 Well, let's go to fs-v4, which is the file system example we did last time. 19 00:01:30,436 --> 00:01:34,636 Now when we designed the functions in this file, because we had mutually 20 00:01:34,636 --> 00:01:39,256 referential types We always ended up with 2 functions, with these kind of silly 21 00:01:39,256 --> 00:01:44,800 names there was some data element and some data LOE. 22 00:01:44,800 --> 00:01:50,585 And really, from the perspective of someone who's holding onto an element and 23 00:01:50,585 --> 00:01:55,906 wants to sum all the data. They don't care that there's two 24 00:01:55,906 --> 00:01:58,332 functions. They really only want one function and 25 00:01:58,332 --> 00:02:02,159 they would like it not to have this silly name. 26 00:02:02,159 --> 00:02:05,800 But the function they want to call some data has to have this helper. 27 00:02:05,800 --> 00:02:13,312 So how can we fix that with local? Well, watch this, I'm going to do it once 28 00:02:13,312 --> 00:02:16,415 quickly, and then I'll come back and do it slowly. 29 00:02:16,415 --> 00:02:19,949 What I'm going to do is I'm going to say you know really the function that the 30 00:02:19,949 --> 00:02:23,312 rest of the world wants to call, is a single function that operates on an 31 00:02:23,312 --> 00:02:29,956 element. So I'm going to give the rest of the 32 00:02:29,956 --> 00:02:39,303 world a single function that operates on an element. 33 00:02:39,303 --> 00:02:48,212 And I'm going to put the two functions I wrote before as locally defined 34 00:02:48,212 --> 00:02:58,722 functions. [SOUND] I'll press Cmd+I to fix my 35 00:02:58,722 --> 00:03:05,528 indentation. So now the two functions I originally 36 00:03:05,528 --> 00:03:10,968 defined are in there. I gotta go change the name of all these 37 00:03:10,968 --> 00:03:16,062 tests. I gotta get rid of this test, because 38 00:03:16,062 --> 00:03:25,045 some data [UNKNOWN] is not available at top level anymore. 39 00:03:25,045 --> 00:03:43,580 [SOUND] I have to switch to Intermediate student language because I'm using local. 40 00:03:43,580 --> 00:03:48,121 And let's try it. That functions now running. 41 00:03:48,121 --> 00:03:55,347 Now several things are different here. I'll redo the process in a minute with 42 00:03:55,347 --> 00:03:58,547 another example but several things are different here, and let me talk about 43 00:03:58,547 --> 00:04:03,780 them. The first and most important is that the 44 00:04:03,780 --> 00:04:08,050 two mutually recursive functions, the ones with the funny names, are now 45 00:04:08,050 --> 00:04:12,655 encapsulated. They're wrapped up inside this local, and 46 00:04:12,655 --> 00:04:18,998 nobody outside the local can see them. As far as the top level program is 47 00:04:18,998 --> 00:04:22,920 concerned, the only function that exists is sum data. 48 00:04:22,920 --> 00:04:27,670 Sum data encapsulates sum data dash element and sum data dash, dash, loe. 49 00:04:27,670 --> 00:04:33,440 So that's the first difference. The, the mutually recursive functions are 50 00:04:33,440 --> 00:04:38,037 encapsulated. We only see a single global function, 51 00:04:38,037 --> 00:04:41,916 because of that I'm only publishing one signature. 52 00:04:41,916 --> 00:04:47,724 The signature of some data, because of that I have fewer tests because some data 53 00:04:47,724 --> 00:04:55,774 is the only top level function. Now in the encapsulation mechanism, 54 00:04:55,774 --> 00:04:59,550 provided by industrial strength languages, you would still be able to 55 00:04:59,550 --> 00:05:03,530 write unit tests for encapsulated functions. 56 00:05:04,570 --> 00:05:08,332 The fact that we can't write unit tests for encapsulated functions here is just 57 00:05:08,332 --> 00:05:12,028 to keep check expect simple. Being able to write unit tests for 58 00:05:12,028 --> 00:05:15,156 encapsulated functions introduces a number of complexities that aren't really 59 00:05:15,156 --> 00:05:20,364 interesting for us for now. So those are some observational 60 00:05:20,364 --> 00:05:22,950 differences. How's this thing working? 61 00:05:22,950 --> 00:05:27,530 When we call some data, for example with F1. 62 00:05:27,530 --> 00:05:31,967 So we call some data. We evaluate the local, all the renaming 63 00:05:31,967 --> 00:05:35,684 and lifting happens, and then the body of the local calls sum-data dash dash 64 00:05:35,684 --> 00:05:39,253 element. And then that calls sum-data dash dash 65 00:05:39,253 --> 00:05:43,675 LOE and back and forth, and back and forth until it gets the result. 66 00:05:43,675 --> 00:05:48,189 That's produced by the body and the function, sometimes we call this line 67 00:05:48,189 --> 00:05:54,940 here a trampoline because what happens is, we come into the top of some data. 68 00:05:56,170 --> 00:06:01,174 Balance off the trampoline, into the mutually recursive functions. 69 00:06:01,174 --> 00:06:03,290 Boing. It's a silly name, a trampoline. 70 00:06:03,290 --> 00:06:05,340 I think you've the idea pretty well by now. 71 00:06:05,340 --> 00:06:08,480 The computer scientists like silly names. The more, the better. 72 00:06:08,480 --> 00:06:13,292 So there we go, that's a first example of encapsulation. 73 00:06:13,292 --> 00:06:18,425 Now, what I'm going to do again is to do the process of converting multiple 74 00:06:18,425 --> 00:06:25,410 functions into a single function with encapsulation, more slowly. 75 00:06:25,410 --> 00:06:28,140 I'll work through the process more slowly. 76 00:06:28,140 --> 00:06:31,195 Telling you what each step is, and then I'll leave you to do the third example on 77 00:06:31,195 --> 00:06:35,490 your own. So let's go down here to this next one. 78 00:06:36,520 --> 00:06:40,488 The first thing is to identify some functions that are a good candidate for 79 00:06:40,488 --> 00:06:44,209 encapsulation. And what makes these functions a good 80 00:06:44,209 --> 00:06:47,867 candidate for encapsulation is that there's 2 or more functions that work 81 00:06:47,867 --> 00:06:53,196 together. Another way of saying is that there is 1 82 00:06:53,196 --> 00:06:57,798 function that has 1 or more helper functions and the rest of the program 83 00:06:57,798 --> 00:07:04,352 really only wants to call 1 function. In this case, all names operating on an 84 00:07:04,352 --> 00:07:09,230 element is really the only function that the rest of the program wants to call. 85 00:07:09,230 --> 00:07:15,930 All names dash, dash LOE is just a helper for all names dash element. 86 00:07:15,930 --> 00:07:20,400 So the first thing is to identify a candidate that has this property. 87 00:07:20,400 --> 00:07:23,750 That there is a function with one or more helpers, and the rest of the world really 88 00:07:23,750 --> 00:07:29,596 only wants to call one function. Next thing to do is to think of a name 89 00:07:29,596 --> 00:07:32,245 for the function that the whole world will call. 90 00:07:32,245 --> 00:07:37,440 Sometimes one of the functions already has that name, sometimes it does. 91 00:07:37,440 --> 00:07:41,280 In this case, I think all names dash dash element is a lousy name for the rest of 92 00:07:41,280 --> 00:07:45,459 the world to use, so I'm going to make up a new name. 93 00:07:45,459 --> 00:07:54,028 So I put the function header like that. Then, I say Local in an opening square 94 00:07:54,028 --> 00:07:59,220 bracket, and I go after the last of the functions, and I close the square 95 00:07:59,220 --> 00:08:04,002 bracket. I'm not going to fix the indentation yet; 96 00:08:04,002 --> 00:08:14,138 I will in just a second. And then, I write a trampoline 97 00:08:14,138 --> 00:08:16,220 [SOUND]. 98 00:08:16,220 --> 00:08:24,330 That calls the appropriate one of the multiple functions to get things started. 99 00:08:24,330 --> 00:08:27,845 I close all the parentheses. I type Command I to fix the indentation. 100 00:08:27,845 --> 00:08:32,843 So now I've done the encapsulation, I just have to clean up the paperwork. 101 00:08:32,843 --> 00:08:36,625 First thing I have to do is, if there were multiple signatures, now there's 102 00:08:36,625 --> 00:08:40,556 really only one. Now there's just the signature of the 103 00:08:40,556 --> 00:08:46,414 function that the whole world sees. So I'll get rid of the second signature 104 00:08:46,414 --> 00:08:50,648 and I'm going to clean up the tests. Oftentimes this is going to mean getting 105 00:08:50,648 --> 00:08:53,376 rid of the base level tests and I'll talk about the implications of that in a 106 00:08:53,376 --> 00:09:00,067 minute. I'm going to first get rid of the tests 107 00:09:00,067 --> 00:09:03,353 for the function that we don't have anymore. 108 00:09:03,353 --> 00:09:10,160 Then I will do a renaming of all the other tests. 109 00:09:10,160 --> 00:09:18,096 I want to copy that, I'll say Find, come down here, find that, replace it with 110 00:09:18,096 --> 00:09:23,220 that. I'll say re- Replace, Replace, Replace, 111 00:09:23,220 --> 00:09:26,822 Replace, Replace. These stubs, you know, there's two 112 00:09:26,822 --> 00:09:30,918 choices now, I could do that replace, and get rid of the second stub, get rid of 113 00:09:30,918 --> 00:09:38,839 that s, that might be one thing to do. Or you could just delete the stubs 114 00:09:38,839 --> 00:09:42,880 entirely. And now, of course, whenever you do 115 00:09:42,880 --> 00:09:47,844 something like this, you test right away to make sure you didn't mess up. 116 00:09:47,844 --> 00:09:52,526 And I didn't mess up. Now, the way I did that process so 117 00:09:52,526 --> 00:09:56,530 methodically follows this notion of a refactoring. 118 00:09:56,530 --> 00:10:00,250 The idea of a refactoring is, you're going to make a change to a program that 119 00:10:00,250 --> 00:10:07,320 doesn't change its behavior at all. It just changes its structure. 120 00:10:07,320 --> 00:10:11,830 Sometimes you want to do a refactoring and then change the behavior. 121 00:10:11,830 --> 00:10:15,200 You should avoid the temptation to do both of those at the same time. 122 00:10:15,200 --> 00:10:19,756 First, do the refactoring, check and test that the behavior is still correct then 123 00:10:19,756 --> 00:10:22,617 change the behavior. Alright? 124 00:10:22,617 --> 00:10:26,846 Move in small steps. So there we go, that's a second example. 125 00:10:26,846 --> 00:10:31,434 I'm going to come back to talk about the fact that we deleted the base case test 126 00:10:31,434 --> 00:10:35,688 in a minute. But first, I'm going to leave you to do 127 00:10:35,688 --> 00:10:40,572 an exercise, which is to go ahead and do the encapsulation of the fine functions 128 00:10:40,572 --> 00:10:46,070 in the way that I've just done. So have this started in front of you. 129 00:10:46,070 --> 00:10:48,302 You should have have it in front of you and you should've been doing this all 130 00:10:48,302 --> 00:10:51,293 along. But I'd like you to go ahead and do this 131 00:10:51,293 --> 00:10:55,960 one now for yourself, just to practice the whole step-by-step process I did. 132 00:10:55,960 --> 00:11:00,230 And to help you, I'm going to put the steps of the process back on the screen. 133 00:11:00,230 --> 00:11:05,132 And you can also find them in the design recipes on the Using Local for 134 00:11:05,132 --> 00:11:16,136 Encapsulation link. So there's my solution, I basically just 135 00:11:16,136 --> 00:11:24,410 follow that process. I called the top level function, Find, 136 00:11:24,410 --> 00:11:28,370 and I removed one signature, I removed some tests, I renamed the function in the 137 00:11:28,370 --> 00:11:32,130 test. I did the encapsulation. 138 00:11:32,130 --> 00:11:34,890 We did the whole thing and there we go, all tests passed. 139 00:11:34,890 --> 00:11:42,150 Now, we just did this for all three of the functions operating on this arbitrary 140 00:11:42,150 --> 00:11:49,163 area tree. And we also know that for functions 141 00:11:49,163 --> 00:11:53,451 operating on mutually referential types, there's always going to be multiple 142 00:11:53,451 --> 00:11:56,990 functions. They are always going to go together. 143 00:11:59,370 --> 00:12:01,370 So that might give you an idea about the template. 144 00:12:01,370 --> 00:12:05,242 It gives me an idea about the template, and here's the idea it gives me. 145 00:12:05,242 --> 00:12:15,120 Which is maybe what I should do is pre-encapsulate the templates. 146 00:12:15,120 --> 00:12:20,720 Maybe I should make a function like this. Maybe I should write the temple like 147 00:12:20,720 --> 00:12:31,580 this, define fn for element B, local, and then both of these template functions. 148 00:12:31,580 --> 00:12:47,096 Get rid of this comma in here, and now I have a pre-encapsulated template. 149 00:12:47,096 --> 00:12:52,388 You could do that it would work out pretty nicely actually let's see what the 150 00:12:52,388 --> 00:12:57,932 consequences of having that template are let's copy that template and let's go 151 00:12:57,932 --> 00:13:03,224 back down to for example some data and pretend for a minute that we don't have 152 00:13:03,224 --> 00:13:11,717 the solution. Pretend that we're just at the copy 153 00:13:11,717 --> 00:13:16,129 template stage, I've now just copied the template, well, what am I going to do? 154 00:13:16,129 --> 00:13:22,128 I have to rename the template. Notice, I don't have to bother to rename 155 00:13:22,128 --> 00:13:28,540 the internal functions. The rest of the world isn't going to see 156 00:13:28,540 --> 00:13:31,783 those internal functions so they could be called strawberry and banana for all the 157 00:13:31,783 --> 00:13:35,982 rest of the program cares. It's perfectly fine for them to be called 158 00:13:35,982 --> 00:13:40,300 fun for element and fun for loe. You don't have to rename them at all. 159 00:13:41,380 --> 00:13:46,340 And now I just code to the examples. Let's see, if I'm summing the data, I 160 00:13:46,340 --> 00:13:51,926 don't care about the name. I'm going to add the data of this 161 00:13:51,926 --> 00:14:00,220 element, together with the natural mutual recursion. 162 00:14:00,220 --> 00:14:06,502 The base case result is zero, and in the combination step here, I also put a plus. 163 00:14:06,502 --> 00:14:16,952 And I'll run it and here I very, very quickly got to the encapsulated solution. 164 00:14:16,952 --> 00:14:24,590 It's almost exactly the same as this encapsulated solution. 165 00:14:24,590 --> 00:14:28,454 It's just that here, the inner functions, the encapsulated functions, still have 166 00:14:28,454 --> 00:14:32,206 their template name rather than having a custom name, but as I said, that doesn't 167 00:14:32,206 --> 00:14:36,509 matter. What's the advantage of doing it this way 168 00:14:36,509 --> 00:14:40,123 rather than starting with separated templates? 169 00:14:41,250 --> 00:14:45,652 Well the big disadvantage of doing it this way is that I wasn't able to have a 170 00:14:45,652 --> 00:14:52,074 base case test, during development. Because I'm not able to write a test for 171 00:14:52,074 --> 00:14:56,004 just the LOE function. I have to write a test for the whole 172 00:14:56,004 --> 00:14:59,736 function. So here's what I would say about that. 173 00:15:01,580 --> 00:15:05,672 At this point in the course, it's still important to have unit tests it's always 174 00:15:05,672 --> 00:15:10,420 important to have unit tests. And the basic idea of testing simple 175 00:15:10,420 --> 00:15:17,232 cases first is always important. But, you're getting sophisticated enough 176 00:15:17,232 --> 00:15:22,360 now that you may not need to actually have the absolute base case test first. 177 00:15:22,360 --> 00:15:25,360 It might be enough for you to have a simple test first, a test that's 1 away 178 00:15:25,360 --> 00:15:30,361 from the base case. And if that's true for you, then you can 179 00:15:30,361 --> 00:15:36,378 go ahead and. Design a function like some data using 180 00:15:36,378 --> 00:15:42,132 clue encapsulated templates. If you're not comfortable with that idea, 181 00:15:42,132 --> 00:15:47,490 if that gives you the willies, then don't start with pre-encapsulated templates. 182 00:15:47,490 --> 00:15:51,577 Start with separate templates, start with a full set of unit tests with base case 183 00:15:51,577 --> 00:15:56,648 tests in everything and then once your program works Encapsulated. 184 00:15:56,648 --> 00:16:00,759 That's fine. For that matter, you don't absolutely 185 00:16:00,759 --> 00:16:06,196 have to encapsulate it. But let me tell you why encapsulation 186 00:16:06,196 --> 00:16:09,996 matters. Remember what I said, cars have 8 million 187 00:16:09,996 --> 00:16:15,702 lines of code in them. In 8 million lines of code, how likely do 188 00:16:15,702 --> 00:16:19,606 you think it is that two different programmers once wanted to use the same 189 00:16:19,606 --> 00:16:27,940 function name? Is it 99% likely or 100% likely? 190 00:16:27,940 --> 00:16:32,695 It's one of those two. And that's what encapsulation does. 191 00:16:32,695 --> 00:16:36,985 It lets one programmer have a whole bunch of functions with whatever names they 192 00:16:36,985 --> 00:16:41,145 want and they just publish to the rest of the world a small number of functions 193 00:16:41,145 --> 00:16:46,784 with really good names. All of their internal functions are 194 00:16:46,784 --> 00:16:50,648 totally hidden and that lets them do name, what's called name space 195 00:16:50,648 --> 00:16:55,133 management, it lets them deal with this naming problem, and it also makes sure 196 00:16:55,133 --> 00:17:02,580 that other programmers don't call functions that they're not Supposed to. 197 00:17:02,580 --> 00:17:07,588 You know about the 2htdp image library. And really, what's going on when you say 198 00:17:07,588 --> 00:17:11,996 require 2hdtp/image is, what you're saying is, you're talking to the 199 00:17:11,996 --> 00:17:16,860 encapsulation mechanism built into Racket, and you're saying Hey, grab the 200 00:17:16,860 --> 00:17:23,400 whole rack of stuff, that's the two HTTP image library. 201 00:17:23,400 --> 00:17:30,560 And let me see only the published names. There's also going to be a lot of 202 00:17:30,560 --> 00:17:34,185 unpublished names. As a general rule, when you grab a module 203 00:17:34,185 --> 00:17:40,620 of code, there's far more unpublished or hidden names than published names. 204 00:17:40,620 --> 00:17:43,409 And that's what a require mechanism is letting you do. 205 00:17:44,470 --> 00:17:48,124 Is it's letting you say, just let me see the public names, leave all the other 206 00:17:48,124 --> 00:17:51,698 ones encapsulated. So there you go. 207 00:17:51,698 --> 00:17:57,393 That's the first use of local, which is to support encapsulation in the teaching 208 00:17:57,393 --> 00:18:02,125 languages. Other languages have more sophisticated 209 00:18:02,125 --> 00:18:06,125 encapsulation mechanisms, but the basic idea is always the same. 210 00:18:06,125 --> 00:18:10,681 Take two or more functions, hide all the helpers inside a capsule so that the rest 211 00:18:10,681 --> 00:18:15,169 of the world sees only the functions that are really appropriate for the rest of 212 00:18:15,169 --> 00:18:18,680 the world to call.