WEBVTT 0 00:01.380 --> 00:02.130 Welcome back. 1 00:02.910 --> 00:07.240 Lastly, you have rewritten the log parser using structs. 2 00:07.260 --> 00:09.540 Now it's time to rewrite it once more, 3 00:09.540 --> 00:11.250 using functions. 4 00:11.250 --> 00:16.930 Let's talk about how to design a program using functions. When designing a program, 5 00:16.950 --> 00:20.520 you need to find out its unique responsibilities. 6 00:20.520 --> 00:27.030 You can do so by looking at the verbs. The log parser program parser a log file and aggregates the 7 00:27.030 --> 00:29.220 visits by domains. 8 00:29.220 --> 00:31.500 Let's take a look at what it does. 9 00:31.560 --> 00:34.680 First it parses the log file line by line. 10 00:35.580 --> 00:42.510 If it cannot parse a line, it returns an error, otherwise it puts the domain name and the number of visits 11 00:42.510 --> 00:44.020 to a result value. 12 00:44.400 --> 00:48.390 Next, it saves the unique domain names to a slice. 13 00:48.390 --> 00:51.980 It also saves the parsing results to a map. 14 00:51.990 --> 00:56.700 It also keeps track of the total visits across all the domains. 15 00:56.700 --> 00:59.920 Lastly it prints a report per domain. 16 00:59.970 --> 01:03.450 So in this lecture this diagram will be your guide. 17 01:03.450 --> 01:07.620 I'm going to show you how to split the log parser program to functions. 18 01:07.620 --> 01:10.410 Let's get started. 19 01:10.440 --> 01:17.470 The main goal of this lecture is to move the parsing logic out of the main function as much as possible. 20 01:17.700 --> 01:21.470 Okay let's take a look at the code. 21 01:21.550 --> 01:27.790 Here it creates a parser but this shouldn't be the responsibility of the main function so it is better 22 01:27.790 --> 01:33.160 to move this to its own function. First let me copy it from here. 23 01:33.160 --> 01:36.880 Here I'm going to create a new parser using a function. 24 01:36.880 --> 01:43.020 Next I'm going to create a new file and I'm going to name its parser.go. Here, 25 01:43.120 --> 01:49.630 I have created a new file because I want to keep the code clean and separated from the main function. 26 01:49.830 --> 01:53.930 All right inside the file I'm going to say package main. 27 01:53.940 --> 01:58.970 Next, I'm going to declare the function like so. It returns a parser of value. 28 01:59.190 --> 02:03.630 So, I'm going to return a new parser value by pasting the code that I've copied. 29 02:04.170 --> 02:06.490 This is a constructor function pattern. 30 02:06.720 --> 02:11.040 Unlike other OOP (Object-Oriented-Programming) languages, Go doesn't have constructors. 31 02:11.040 --> 02:13.670 However, you can imitate them (OOP constructors) using a function. 32 02:13.770 --> 02:20.370 It is called a constructor function because it constructs the initial value and returns it here. 33 02:20.370 --> 02:24.610 It makes the parser value ready to use and returns it. 34 02:24.660 --> 02:26.680 Okay let's test it. 35 02:26.880 --> 02:27.610 Cool. 36 02:27.630 --> 02:29.140 It still works. 37 02:29.160 --> 02:32.730 Okay let's move all the structs to the parser file as well. 38 02:32.790 --> 02:37.290 They make unnecessary clutter here. It still works. 39 02:37.330 --> 02:38.020 Good. 40 02:38.020 --> 02:40.540 Okay let's take a look at the code again. 41 02:40.630 --> 02:42.790 I think it does a lot of things indeed. 42 02:42.790 --> 02:44.160 Let's split it more. 43 02:44.410 --> 02:47.010 Let's start with the parsing logic here. 44 02:47.050 --> 02:48.340 Let's cut it from here. 45 02:48.520 --> 02:49.810 Inside the parser file. 46 02:49.810 --> 02:52.250 I'm going to declare a function named parse, 47 02:52.270 --> 02:55.320 like so. Let's paste the code here. 48 02:55.360 --> 03:02.050 Of course, there are errors now. in.Text() doesn't work here because the in variable is in the main 49 03:02.050 --> 03:02.770 function. 50 03:02.860 --> 03:09.850 So let's declare a string parameter to get the current line from outside and let's replace the in.Text() 51 03:09.850 --> 03:13.660 call here with the line variable instead. 52 03:13.660 --> 03:18.610 Now it says there is no p variable in the scope of the new parser function. 53 03:18.820 --> 03:22.940 So let's declare another input parameter to get it from outside. 54 03:23.050 --> 03:25.070 Doing so fixes the errors. 55 03:25.090 --> 03:29.110 Now it says the domain variable is declared but not used. 56 03:29.110 --> 03:33.910 The purpose of the parse function is to parse the log line and return a result value. 57 03:33.910 --> 03:38.590 So let's do it. Since it returns a result type, 58 03:38.590 --> 03:40.530 so it also needs to declare it. 59 03:40.540 --> 03:41.100 Let's do that. 60 03:42.150 --> 03:43.830 Now there are two more errors. 61 03:43.830 --> 03:48.120 The function needs to return a value but it doesn't do so here. 62 03:48.150 --> 03:51.600 Let's declare a new result value above and return it instead. 63 03:53.420 --> 03:53.720 Okay. 64 03:53.730 --> 03:59.350 This fixes the errors. By the way, I think I can use the same result value throughout the function. 65 03:59.400 --> 04:05.280 Let me show you. Instead of saving the result to these variables, I'm going to save them to the parsed 66 04:05.280 --> 04:06.200 variable. 67 04:06.300 --> 04:12.900 First I'm going to change the visits variables to parse.visits like so. Now there is an error because 68 04:12.930 --> 04:18.990 I cannot declare a struct field here because it's already been declared. Before fixing that, 69 04:19.000 --> 04:22.280 let's also change it in the last return statement. 70 04:22.320 --> 04:24.590 Okay now let's fix the error. 71 04:24.660 --> 04:29.010 Let's create an empty error value next to the parsed variable. 72 04:29.010 --> 04:31.560 Now I'm going to change the declaration to assignment. 73 04:32.070 --> 04:34.920 Let's also change the domain variable like so. 74 04:34.980 --> 04:40.390 And lastly, let's return the parsed variable directly because now I already initialized it. 75 04:40.550 --> 04:40.960 All right. 76 04:40.960 --> 04:41.650 Done. 77 04:41.670 --> 04:47.000 Now it's time to return back to the main function and call this function (parse()) from there. 78 04:47.010 --> 04:53.440 Here I'm going to call the parse function by passing the parsed value adding the current line like so. Let's 79 04:53.550 --> 04:56.970 also save its results to a variable. 80 04:57.100 --> 05:02.920 Of course there are errors now because the domain and visits variables are not declared. 81 05:03.160 --> 05:10.760 I can easily fix this problem by declaring the variables using the parsed value like so. Alright, let's 82 05:10.760 --> 05:19.510 try it. Awesome! It still works. It catches the errors as well but it shouldn't deal what to do with the 83 05:19.510 --> 05:20.400 errors. 84 05:20.410 --> 05:26.320 It should only return them. For example, other programmers were using this function and maybe they were 85 05:26.320 --> 05:28.580 logging to errors to somewhere. 86 05:28.720 --> 05:34.540 If you print the errors directly they cannot process the errors and they will be very upset. 87 05:34.540 --> 05:36.720 So let's return the errors instead. 88 05:36.820 --> 05:39.940 To do that I need to declare an error result 89 05:39.970 --> 05:42.370 value as well, like so. Good. 90 05:42.390 --> 05:49.990 But how can you create an error value? You can easily do that by replacing the print calls with 91 05:50.080 --> 05:54.670 Errorf calls like so. Errorf works just like Printf. 92 05:54.760 --> 06:00.340 However, instead of printing an output it returns it as an error value. 93 06:00.340 --> 06:07.310 Okay, now I'm going to assign them to the error values. There are warnings because the error messages 94 06:07.400 --> 06:12.510 shouldn't include a new line. It's because the error messages can be chained. 95 06:12.680 --> 06:18.200 You can return an error and another error can add its own error message to it. 96 06:18.320 --> 06:21.240 So let's remove them. 97 06:21.240 --> 06:25.890 OK, now I'm going to return the error values like so. Good. 98 06:25.900 --> 06:31.800 Now let's handle the error from the main function as well. I'm going to get it from the parse function 99 06:31.800 --> 06:35.420 like so. Then I'm going to check for the error like so. 100 06:36.350 --> 06:37.610 If there is an error, 101 06:37.610 --> 06:40.580 I'm going to print it and return from the function. 102 06:40.580 --> 06:43.930 Remember the main function doesn't return anything. 103 06:43.970 --> 06:48.740 That's why I can return from it without any arguments to the return statement. 104 06:48.740 --> 06:51.670 The main function runs the whole show. 105 06:51.710 --> 06:55.750 So when I return from it, the program terminates by itself. 106 06:55.760 --> 06:58.940 Let's see whether it is gonna handle the errors. 107 06:58.970 --> 07:03.070 I'm going to run it with log_err_missing, then I'm going to run it with 108 07:03.080 --> 07:04.590 log_err_str 109 07:04.670 --> 07:08.490 And lastly I'm going to run it with log_err_negative. 110 07:08.580 --> 07:09.950 It works. 111 07:10.080 --> 07:12.060 Let's go back to the parse function. 112 07:12.060 --> 07:13.880 I'm going to show you something. Here, 113 07:13.950 --> 07:15.720 I declare empty variables, 114 07:15.720 --> 07:17.580 then I return them three times. 115 07:18.300 --> 07:20.370 Fortunately, there is an easier way. 116 07:20.370 --> 07:20.990 Let me show you. 117 07:21.930 --> 07:23.950 Let me add names to the result 118 07:23.960 --> 07:25.610 values like so. 119 07:25.710 --> 07:28.680 Next I'm going to remove these declarations. 120 07:28.710 --> 07:36.010 Lastly, I'm going to call the return statements without any arguments like so. As you can see it works. 121 07:36.130 --> 07:37.120 But how? 122 07:37.120 --> 07:38.050 Let me explain. 123 07:39.860 --> 07:42.680 These are the names of the result values. 124 07:42.680 --> 07:47.570 When I do so, they become variables inside the parse function. Behind the scenes, 125 07:47.570 --> 07:49.350 Go declares them like so. 126 07:50.350 --> 07:54.470 And when I call the return statement, Go automatically returns them. 127 07:54.550 --> 07:57.220 This is called a naked return. 128 07:57.250 --> 07:59.310 It only works when you use the named 129 07:59.350 --> 08:01.550 result values. Behind the scenes, 130 08:01.570 --> 08:04.820 it works similar to returning them manually like so. 131 08:05.140 --> 08:08.030 That is why the parse function still works. 132 08:08.150 --> 08:08.890 All right. 133 08:08.970 --> 08:10.130 That's all for now. 134 08:10.150 --> 08:11.500 See you in the next lecture.