Transcript from the "Projector CLI App in Go" Lesson
>> We'll go back in, let's get back start. Let's go to Go Lang, again, we're still here but now we're a gopher. So it feels a little bit different. I think how Go handles maps and values, I do like it a bit better than how TypeScript does it, but it doesn't have that nully coalescing operator that makes it really fantastic.
[00:00:18] So it's kinda like a trade off there, both really great, both okay. So we're gonna do the exact same thin,g we'll create a projector.go and it's gopher time. In my head I heard it's hammer time. And it was fantastic. So let's jump over to Go. Inside of projector, let's create projector.go.
[00:00:40] All right, exact same thing. We have to set the package name, of course, to be package projector and now we can start adding things. So we already kinda have something in our mind that we want to build right. We should be having a data object, so let's just do that now.
[00:00:53] type Data struct, and it has one key, projector, correct? So I'm gonna keep it uppercase, and you'll see why here shortly. I'm gonna go Projector. So we're exporting this, why would we export this, right? We don't have to export this, it's kind of is the implementation details of our item.
[00:01:14] Well, the reason why after we do map[string], so, by the way, this is the map syntax. It's a lot like, oop, not this one, projector, where are you? It's a lot like this, right? It's gonna look very, very familiar to this, but instead it's map key string and then you put your value right here.
[00:01:36] And so we're gonna do another map cuz our value is another map, right? And so that'll be a map[string]string. There we go. It's actually not that hard, but it looks funny. The first time you look at it, you're like, gosh, there's a lot of mapping going on there.
[00:01:49] But it's actually pretty straightforward. And then we're gonna add in one more piece of item, we're actually gonna go like this, json, and then we'll go projector, right? Meaning that, hey, when you serialize this to JSON, the key is lowercase p projector, right? Because it can't do a one-to-one translation and you can even have them non one-to-one translations, right?
[00:02:10] I don't actually have to have this key perfectly match up. I'm just telling it what key to grab out of the JSON. All right, so now that we have that struct, we should also have a second struct which is gonna be your projector, right? Now remember, in Go we don't have classes, we have structs.
[00:02:31] And then we can attach methods to structs. So always, it's just thought about a little bit different. But this really lines up with what we wrote in TypeScript. So what were the properties we had, we had two properties, right? We had config, and we had data. So we'll just create the exact same ones.
[00:02:44] We have config, which is gonna be a Config, and then we're gonna have data, which is gonna be a Data. There we go, we're done with our projector. So we're moving along pretty fast already. So we have the exact same set of five methods, is it five? Four methods that we want to do on our projector from the previous one, which is gonna be func.
[00:03:07] Now, remember, we have to do what is referred to as the pointer receiver, right? So this is how you attach something. I want a pointer to projector to effectively be the this variable. That's what you're saying right there. And so I can refer to the this variable with p.
[00:03:23] There's way to call these functions without calling them directly on the object, we're not gonna do any of that. But that's all it's saying right there, it's just I expect this to be a pointer p, which also allows us to mutate the object when we need to. So, very important do.
[00:03:37] So let's just do GetValue, also remember uppercase is important here. We have to do that. So now what do we return? Well, we're gonna have to return something, correct? We have to return some value out of GetValue. Now this is kind of a design decision we're gonna need to do with Go because we could return a pointer to a string.
[00:03:57] And it could be nil if there's no string, or it could be a string if there is a value. Not, I think, the greatest version. So what we're gonna do is we're actually gonna take a little note from map. Whenever you access a value in map, it actually returns two values, not one.
[00:04:11] It returns the value you're looking for or it returns an ok. And it returns an ok that is a boolean that, hey, if we found the value and this is actually the value. So we can do the exact same thing. That means we can just return a simple empty string and a boolean to say, hey, this is actually it or no, we didn't find it at all.
[00:04:31] So let's go string, and bool, awesome. So now we're actually gonna do that, right? So that means if we didn't find it, we effectively do false, right? That would say, hey, we didn't find it, it's an empty string, whatever. I think that's a pretty good way to go about it.
[00:04:47] So, of course, we need a key, which is gonna be a string. Remember, if you're from TypeScript land, you're gonna be adding in a lot of colons. And it's just a battle, you just gotta remember, it's just gonna take a little bit to get used to. And if you have an LSP, it tends to crash pretty hard, it won't give you a lot of other errors if you have a syntax error.
[00:05:04] So you kinda have to get over the syntax hump before you get the LSP errors. All right, so exact same thing. We're gonna go to our data, we're gonna see if it has this value. And then we're gonna also have to walk up the tree until we hit the end.
[00:05:22] And so this is probably gonna be very, very similar, cuz my guess is that Go is gonna operate in largely the same way. So we have one or two places we can kinda check for any path in utilities, right? We have path, right, this tends to have a lot of nice things when it comes to that.
[00:05:36] So we have Base, you wanna guess what base does? I mean, besides what the documentation it's saying right there, it's equivalent to base name, right? You wanna guess what Dlr does? Well, it's the equivalent of dirname which means it probably even operates identical as Dlr. And you can kinda tell right here, this really gives away the game.
[00:05:54] It's gonna keep on returning a slash. And so I'm just gonna make the guess that it's gonna keep returning a slash. If we're wrong, we're gonna loop forever. If we're not wrong, we'll be successful. It's really great, first tries, you can only do this first time. So the first thing we're gonna do is we're gonna do, of course, curr :=P.config., let's see, present working directory, that's right.
[00:06:17] And so we have to do a little prev, it's just gonna be an empty string. And now we're gonna do the exact same kind of loop. But with Go, there's some things. You'll notice that while doesn't work out, which means they definitely don't have the log loop do while, while which is the greatest loop of all time.
[00:06:34] That means we're gonna have to either do an infinite loop which is done via for. Or we can do a little trick, right? There's no initialization, correct? Current should not equal previous, and there is no incremental operation. Well, look at that, we kind of just did it, right?
[00:06:56] We just created a while loop effectively, couple extra semicolons in there and boom, we have a while loop. Awesome, so let's just take care of the exit condition first. prev = curr, curr = path.Dir(curr). Awesome, so we now have the exit conditioning happening, we're feeling pretty good. And now we just need an out variable.
[00:07:19] So I'm gonna go like this, out equals this, and found := false, return out, found, right? Awesome, we have it kind of all set up. Now it's really just checking the map, getting each value out at this point. This would be pretty straightforward. Well, remember I said that Go actually, I think, does a pretty good job handling these.
[00:07:43] Let me show you how it looks. So we're gonna do an if statement. And we're gonna do this, we're gonna go, here's the directory that we wanna get out cuz remember, we first start with the directory and then we look for the key values. And we're gonna do this ok, and then I'm gonna go p.config, oop, not config, wrong one, data.Projector, and then I can pass in curr.
[00:08:05] And then I'll do a semicolon and say ok. Effectively what this allows you to do is it allows you to make an assignment and then test a condition. And this is really nice when it comes to a lot of these operations because now I have dlr available only within the scope right here.
[00:08:20] And it happens to be the value that we're looking for. So we have confidence, we can just move forward, so I can do it again. If value, ok := p.data, we don't have to do any of that. We can just go like this, dir[key]; ok. So if we do this, awesome.
[00:08:38] If we're within this second if statement, we have found the value, we're good to go. So we'll do this, let's see, out = value, found = true, and, of course, a break. If you don't do that, you'll get the leftmost value as opposed to the rightmost value. Little painful, did that when I was practicing once.
[00:08:56] Awesome, we now have everything done, we've done GetValue. That was pretty easy, right? That wasn't too hard, I don't think. All right, let's do GetValueAll, have we done that one yet? No, we haven't. So let's just do the exact same thing but let's do a nice func. Look at that, it even gives me all this beautiful stuff.
[00:09:15] GetValueAll, and this thing needs to return back out something, correct? It needs to return out, take a moment, try to remember the syntax, we did it once up above. Try to recall with your brain, let it set in. I think it's always good to take that time to try to remember it, not just go straight up for the answer which is right above me.
[00:09:33] For those that don't know, we obviously have to return out a map[string]string, right? We're returning out the merged data. So let's do that now. Of course, out is gonna have to equal a map[string]string, and we'll just create it right there. Awesome, and then we'll just return this thing, because if there's no values to be found, we still return an empty object.
[00:09:58] Great, at this point, we need to do the exact same thing we did before, which is we need to walk the directory path and then we need to go backwards through it. Because we want the least important values to be grabbed first, and we want the most important values to be grabbed last.
[00:10:14] So let's do the exact same thing. So I'm gonna go like this, paths equals a string array. And then I can just jump up here. I can just grab this whole thing. Again, we could be too clever by half and come up with a great way to kinda reduce this duplicated logic.
[00:10:34] Come up with some sort of sweet iterator, but we're just not gonna do that, paths.append. Of course, remember, it's not .append. I say that in my head cuz that's what it reminds me of. And we're just simply going to take paths, append current to it, reassign it, and just walk up until we're done.
[00:10:52] And then afterwards we just need to walk backwards through this list, shouldn't be too hard. So we can do something as simple as i := len(paths), there may even be a way to do this using the range operator. Awesome, so let's walk backwards through, we get the length, -1 from it.
[00:11:12] i has to be greater than or equal to 0. There's also a sweet kind of CPU optimization if you ever compare something to 0, because 0 is a special register value. Anyways, fun fact, and then of course, we're gonna wanna do i --, and now we have a beautiful walk right here.
[00:11:29] And all we have to do is grab the value out at that location, and merge it into the map, and then do that over and over again. Now remember, if you Stackoverflow this question, what are you gonna find? Well, it's really easy to merge a map. You just write it yourself.
[00:11:45] Most of Go is just write it yourself. That's why I defaulted to not even really looking up if range could walk backwards. That sounds like a just write it yourself kind of mantra of Go, and so that's what we're gonna do. We're gonna write a quick little map merging, it's pretty straightforward to do.
[00:12:01] Let's go here and do that. So, of course, the first thing we need to do is, does this value exist? So again, dir, ok := p.data.Projector. And then we're gonna use, let's see, what is it? paths[i], right, there we go. Does this thing exist? Well, I hope it does.
[00:12:23] If it does exist, we now have this beautiful map[string]string. We just need to walk through that thing and merge the values into our out. So it's actually pretty easy to do. So we can have our key, our value equals range dir, and just go out[k] = v, there we go.
[00:12:42] That's all you have to do to merge a map, it's really not all that bad. Pretty clean syntax right there. And so that is no different than the old object assign, right? It's just slightly less. This for me, pretty nice right here. But is that really that hard?
[00:12:58] No, but it is the Go mantra. You'll always see this about Go is that they don't include a ton of helper items. Is that bad? Is that good? I think that's kind of, the argument that I've heard is that it's geared towards people that are more junior. And so therefore they want you to understand the cost of every operation.
[00:13:16] So if you did a merge map, do you realize that you have to walk through every single key and value and do that? That's kind of what I've heard to be the general argument for why they do it that way. Is that a good mantra, is that the right one?
[00:13:28] That's up to you to decide, not me. So there we go, we have this beautiful code. And we should be able to just return out, and this should just work. Fantastic, we're looking good.