Go Time – Episode #269
The bits of Go we avoid (and why)
with Mat, Jon & Carl
The panel discuss the parts of Go they never use. Do they avoid them because of pain in the past? Were they overused? Did they always end up getting refactoring out? Is there a preferred alternative?
Fastly – Our bandwidth partner. Fastly powers fast, secure, and scalable digital experiences. Move beyond your content delivery network to their powerful edge cloud platform. Learn more at fastly.com
Fly.io – The home of Changelog.com — Deploy your apps and databases close to your users. In minutes you can run your Ruby, Go, Node, Deno, Python, or Elixir app (and databases!) all over the world. No ops required. Learn more at fly.io/changelog and check out the speedrun in their docs.
Notes & Links
|1||00:00||It's Go Time!|
|3||12:00||Mat avoids new()|
|4||17:20||Jon avoids full slice expressions|
|5||20:53||Carl avoids bare returns|
|6||24:22||A linter up in your kitchen|
|7||26:59||Mat tries not to panic|
|8||30:52||Jon avoids labels|
|9||35:13||Templates, globals, init|
|10||38:40||Prank Time (+ more bad ideas)|
|11||42:57||Jon tells a story|
|12||44:20||Useless uses of generics|
|13||47:41||Jon doesn't use internal packages|
|14||51:17||It's time for Unpopular Opinions!|
|17||59:04||Next time on Go Time|
Click here to listen along while you enjoy the transcript. 🎧
Hello, and welcome to Go Time. I’m Mat Ryer. Today we’re talking about the bits of Go that we tend to avoid, whether deliberately or by happenstance; perhaps we’ve been burned in the past, or maybe there’s just an alternative that we’d prefer. What’s wrong with that…? Well, to help me find out what’s so wrong with it, I’m joined by Jon Calhoun. Hello, Jon.
Hey, Mat. How are you?
Good. Good to see you, mate, as always. How have you been?
We’re also joined by Carl Jonson. Carl’s back. Hello, Carl.
Hey. It’s great to be back. today I did not wash my hair with Red Bull, so maybe I will be slightly coherent in this episode. We’ll see… No promises.
We’ll see indeed. Okay, so we’ll kick off with an icebreaker; it’s a new section I was thinking about we could do, like ICEBREAKERS. That’s probably just the theme – it’s probably me just saying that, but with reverb added. So it’s like “ICEBREAKERS!” And that’s it. That’s enough.
Just waiting for like a copyright or trademark infringement… Isn’t that like a mint?
Yeah, it could be. If that is a mint, depending on how litigious, that section may get renamed in the future. But fingers crossed for ICEBREAKES! Jon, people say to me “We love it when you make a joke and then Jon carries on as if nothing has happened, like some kind of robotic Android.” I know you’re not a robot, mate, because they can actually appear quite human these days… But if you were a robot, what would be the one robotic superpower that you’d look forward to using?
I don’t know if it’s a superpower, but some antivirus software would be pretty sweet right now… Since I currently have COVID.
Oh, no, that’s terrible. Sorry to hear that.
It’s all good. Something like that would be pretty sweet. It would also suck to get somebody like “Hey, we’re gonna disable the use of your arms unless you send crypto to this address.”
[laughs] Yeah. There’s just pros and cons, because you’ve got that, those problems, but also, you can jump really high. So…
Can I though?
Yeah… Have you seen them?
I’ve gotta get back into looking at these – whoever makes all those crazy robots.
Oh, the Boston Dynamics [unintelligible 00:03:06.04]
Yeah, Boston Dynamics.
Yeah. I don’t know if they have any videos of their dogs jumping really high. But maybe.
Yeah. They’ll get there. I mean, this is – I’m talking in five years. I’m not talking today, technology.
I’m talking about the kind of robotics times we’re gonna have in five years’ time. Probably brilliant; really strong legs, good, strong backs… Just the whole package, probably. And it can do anything. Carl, what would you do when you’ve got eventually your robot body, and you’ve had your consciousness downloaded into it?
I think what I would do is I would be like really literal about common expressions and idioms… So just like extremely literal about it. If somebody’s like – oh, I can’t even think of an idiom now… But if somebody’s like “Get out!” and then I’d be like “Okay, I’m getting out.” And then I would just leave the room, you know?
Like you’re about to go do a play and somebody says “Break a leg.”
Yeah. Then I would break their leg physically, with my robot strength.
Yeah… You’ve gotta be careful when you’re talking to these robots.
I think what I’ve been learning from ChatGPT is all the stuff in science fiction that like struck me as like totally implausible, and – you know, it’s like, you can’t really have the three laws of robotics. That doesn’t make any sense. Or robots wouldn’t be confused by liars, paradoxes and riddles, and stuff… And with ChatGPT it’s like “Oh no, that’s all real. Yeah, robots are like totally confused by this stuff.”
I did a thing where I asked ChatGPT – you know, the classic riddle of the Sphynx is what has four legs in the morning and two legs in the day, and three legs at night? But I asked it what has three legs in the morning, and two legs at day, and four legs at night, and it was like – it not only answered it as if I had asked the normal riddle, which is fine… If you said that to a person, probably they would get confused and like say, “Oh, you mean a person?” But then it was like “Yes, you mean a person, because a person has three legs when they are a baby and crawling on their hands and knees, and four legs when they’re an old person and using a cane.” And it like not only explains it, but it explains it wrong, but using like the wrong things… It just gets tripped up on it.
So yeah, I’m really looking forward to like robots who can’t use contractions, and just have to be very formal all the time… And they’re not really supposed to be formal, it’s just that’s how the human reinforced learning made them be…
I love that though. I think that kind of way of talking - that’s the way you want to go. It reminds me of genies; like, you’ve gotta be really careful when you’re talking to a genie.
Oh, yeah. It’s super-literal.
[00:05:50.09] Yeah. And they’re like – you need a lawyer. You need to have a lawyer with you if you ever come across a genie. Because they will get you. You’ll be like “I want to have a million dollars”, and then it’ll crush you with a million dollars, or something.
Yeah, they’ll put a million pennies around your head. My thought for the genie is you use your first two wishes to like set up your third. So like your first wish is like “I wish to know what the perfect wish is”, and then like your second wish is like “I wish to say the perfect wish, without messing it up somehow, or getting tongue-tied, or ruining it…” And then finally you do it, you know? But you’ve gotta – you can’t just jump straight to the perfect wish. You’ve kind of got to tee it up with the first two.
Yeah. But you shouldn’t have to do that. You shouldn’t need to waste two wishes just to get around these loopholes that you know it’s gonna try and find.
But it’s the same like with the ChatGPT. You have to 00
I just like how even though genies are completely imaginary…
Jon, you’re ruining it… [laughs] You’re bringing us down!
I’m just saying, they’re completely imaginary, but we’ve decided –
We’ve decided the rules.
…that they’re going to be like these – not evil, but they’re intentionally taking anything we say and twisting it. Like, we’ve just decided that’s what all genies would do if we found one.
I think that’s the thing, though… ChatGPT, because it’s trained on the internet, and it’s trained on fiction, I think at some level it’s like “I should behave like a genie”, or “I should behave like a computer in a story” or something like that. That’s what it thinks is normal. It doesn’t have like eyes, or hands, or legs. It doesn’t interact with the real world, it doesn’t know the difference between truth and fiction other than people saying “This is true and this is fiction”, which sometimes they say wrong. For example, fictional works that say that they’re true when they’re not, and stuff.
So anyway, I feel like this genie stuff is important, because now ChatGPT is going to be like “Alright, when somebody asks for a million dollars, I’m gonna pour a million pennies on their head until they’re crushed flat as a pancake”, because that’s what you’re supposed to do as a magical AI genie.
That’s what’s bound to happen. My elderly neighbor recently - she just said to me, “Maybe I’ll be back.” She’s like “Maybe I’ll be back”, like some kind of tentative Terminator… And it’s like, I quite liked the idea of the machines going the other way, and being very uncertain, and not sure, and stuff… It’s like, even at the point of like booleans, it’s like “I don’t know, I can’t decide. I’m not sure.” Just so they become more human.
I mean, that would make coding very weird. You’d have like “if true, if maybe true…”
“If you feel like it.” No, it’s just like with a regular person. You go “If you feel like it.” “If you feel like it, handle the API request.”
No, but that’s a good way to do it, because sometimes it doesn’t work, and sometimes it doesn’t feel like it, and then you have to be like “Alright, if you don’t feel like doing that, wait a little bit, then try again.” Like, it could be a new kind of programming that is just sort of very kind-based. Not kind-based, but you know… Nice…
So Mat, if you had a robotic superpower, what would yours be?
I would love to be able to just randomly facts people. If I could just be like – figure out their number…
I feel like you picked the one superpower that like – it’s a terrible time to have it.
Like, “I can fax anyone” and there’s no fax machines in the world.
Well, but if it’s like, if someone’s there, and they’re checking – like, “Oh, I’m just gonna check this security camera”, and they’re gonna see me doing something on it, I’ll quickly in the back of my head just be like “Blip-blip-blip-blip”, and I’ll fax them, and they’ll be like “Oh, hang on… I’ll just check that in a minute. I’m [unintelligible 00:09:13.05] Then they go and read the fax, and then I’ve got enough time to delete the footage.
Yeah, because the fax is so slow. Like, they’re basically taken out for like five whole minutes handling the fax.
Exactly. And they’ve got to turn the wheel on the fax machine to make it go… So it’s “Busy. Busy, busy, busy. Oh, what’s going on meanwhile?” Just me clicking in the back, deleting footage…
I don’t know what fax machines have a wheel you turn. I’m so confused right now.
[00:09:42.21] I can’t remember them, really. I’ve not done any research. Carl, I’ve got another icebreaker for you. ICEBREAKER! Patent lawsuits possibly pending, because we think that might already be a thing… But assuming not, Carl, your icebreaker. You are in a cave, dark, [unintelligible 00:09:58.28] cave, it stinks… There’s a little lamp on the floor. It’s got an apple logo on it. So you rub it, and it’s like “Oh, what’s this?” Bloody Tim Cook Genie pops out and says “Right, you can have three wishes.” What do you want for your next iPhone, or your next phone feature? You can have any features you want; you get to pick the next three. Or you can just pick one. What’s your top feature you want for your phone?
I think the top feature would be to bring back the headphone jack on the iPhone 7 shape. I guess iPhone SE. Yeah, just bring back that iPhone SE, but put on a real headphone jack. I’m still mad at them for taking it away.
I think they needed to take it away on the iPhone 10, because the iPhone 10 was like bigger, and edge to edge, and whatever… But they knew people would get mad at them when they did that, so they were like “Okay, we’ll remove the headphone jack on the iPhone 7 first, and we’ll have a year of people being mad about the headphone jack. And then the next year we’ll release the 10, and people will have forgotten about the headphone jack by then.” Which totally worked. It was like a completely successful approach of theirs. But I’m still mad about the headphone jack. I want it back. I wouldn’t even use it. I just want it.
Yeah. It’s weird how they’ve probably got models modeling how angry everyone’s going to be about stuff… And they’ll use that to sort of inform it, and make decisions based on that.
Yeah. I guess it’s probably going to happen, so it’s not a funny one… It’s like the USB-C thing. It’s like, yeah, I guess it’d be good if they went to USB-C, just so it uses the same charger as some other stuff… That’s gonna happen, so I wouldn’t want to waste my wish on something that will happen anyway.
Right. Yeah. Well, you’re wasting your first two wishes normally, just propping up your third one. Tim Cook’s got a lot of legal power behind him, to be fair, so you probably do want to be careful in this case… Well, talking about wishing things here and gone, let’s get into the meat of our subject, or plant-based protein, depending on your preferences. We’re going to talk about what we would get rid of, or at least what we don’t use, what we avoid in Go. And part of this I think is interesting, because - you know, all this stuff we have to learn… And if we can cut out some of that, we can save some of that learning process. Maybe. There’s advantages there. But also, it probably comes for me more down to writing maintainable things.
So it’s always about being very explicit and clear, so that when I come back later, I don’t have to decode anything, and it’s all kind of laid out in front of me. And in that spirit, the first one I probably don’t ever use is the “new” keyword; the “new” keyword. And this is the thing that makes things… So how can you do without it? Well, of course, there are other formats; like, you use the structure name with the curly braces immediately after to instantiate it. You’re usually taking a pointer sometimes, at the start of that… And I like that pattern more, because even if you’re not setting any fields explicitly, it’s the same as if you do. So it’s the same format both ways. It’s not like a different format just because you’re not setting any fields. How do you feel about that? Do you use new?
I do use new, but I mostly use it in generic code, where I’m trying to make this catch on – I think maybe this was on my Twitter; I don’t remember if I tweeted this or I mastodonned it, but one way or the other, I tried to make it a thing to call this construct the “go winking newt.”
So in generic Go - so as of the last year, Go has had generics. One of the kind of issues which I hope that they fixed sometime is that there’s no good way to say “return the zero value of whatever type I’m dealing with”, right? So inside of a generic code statement you might say like “my type is t, and I just want you to return whatever the zero value of t is.” And so there’s a couple of ways you can get around it. And the way that I think most people get around it is they end up typing, “var x, or whatever, t”, so then now you’ve got an x, which is of type t, and it’s the zero value, and then you just say return x. Or var zero t return zero. That’s what most people do.
[00:14:33.02] But another thing you can do, which is hard to talk about on a podcast, because talking about syntax on a podcast is always death, is you can do return star new parentheses t parentheses. And so it’s like the winking newt, the star newt. And if you say that, that will return the zero value of whatever t is.
So if you think about it logically, what it’s doing is it uses new t to create a pointer to t, and then the star means dereference the pointer to t. And since you had just created it, it’s the zero value. And so it’s like this weird little idiom that’s popping up in generic Go code. And that’s basically the only time I use new, is in that particular construct. And really, I’m just doing it because it’s kind of cheeky, that it’s a little newt.
I assume you also just want like a one-liner, like return this versus having to declare the variable up front, and then have it available.
Yeah. You can do it as an expression. You don’t have to like set it up by first having a line for var zero t; you can just do it right in line, asterisk new t.
Yeah. Because it’s no more efficient, is it?
I think if you look at the disassembly, it’s identical, yeah.
Yeah, but you can do it in line. Well, quite a good use of new there…
But I really – I have an issue open on the Go issue tracker; or maybe it’s not my issue… There’s an issue open on the Go issue tracker… Oh, I tried to open one and they closed it as a duplicate. That’s the answer. But there’s an issue to try to create a new identifier called zero. And so you would just type out z-e-r-o, and it would return the zero type of the value. So hopefully, someday that’ll be a thing, and we can stop having the cross-eyed newt. But until then, you have to use the cross-eyed newt.
So with the zero-value thing you’re talking about - was that proposed only for generics, or for everything?
It was proposed, I think, before generics. There was like some talk about how you can use nil in some places, but not others, and maybe you should be able to use nil everywhere… Or yeah, if you’re doing return, whatever, and then an error, it’s nice to be able to just say return zero comma err, something like that.
And then the use case that I also really want it for is for comparison. In the last episode I was on I talked about comparable types, and how some types are comparable and some are not… If you have like a function, you’re allowed to compare it to nil, but you’re not allowed to compare it to other functions. So then when you’re writing generic code, there’s no good way to say “compare this to nil.” You have to use reflection. So if there was a zero value, then you could just say “Alright, compare this to its zero value”, and then it would be okay.
Okay, cool. Jon, why don’t you tell us about something that you barely ever used in your whole life?
So one of them is the full-size expressions. So I don’t know if you’ve seen those or used them at all, but normally, when you do a slice, you have – when you’re trying to get a slice of an array, you might do like a and then the square bracket, and then like the starting index, colon, and the ending index. You can actually add like a third value there, which I believe is essentially the capacity of the new slice you’re creating…
Oh, yeah; inside the square brackets, right?
Yeah. So I’ve seen cases where it’s useful. I’m not trying to say “This definitely shouldn’t exist in the language.” But I don’t ever use it, because in my mind, most people have never seen it in their life. So if you put it in code, the first time they see it, they’re gonna be like “What is going on right now?” And I don’t tend to like things like that, where people are going to see it and just have no idea what’s going on, unless there’s a really good reason for it.
[00:18:11.09] Yeah. And I think a lot of people are going to see it and they would – just naively, you would assume that, “Oh, there’s a third thing in there. Maybe the third thing is like the stride.” Right? So the idea of like you’re skipping, or you’re doing something… I don’t know, I would guess it would be like maybe this makes it go backwards through the slice, or reverses it, or something… I don’t know. I wouldn’t just like naively guess “Oh, this is what controls the capacity.” You really have to look it up, or be told it before you see it.
Yeah, the only clue to that is in the make, because you get the extra arguments. But that’s so different to this. Yeah, I don’t know, it looks like you’re saying - yeah, 1 to 3, definitely 3. I promise. Like, you’re just being very clear again, setting that variable twice. But yeah, it does look quite weird. I genuinely had forgotten that was even there, Jon.
I saw it at one point where it can be useful for – I forget, I think it was like a data structure type thing. Like, if you’re doing a heap, or something, it can be a useful way to make sure you’re not 1) overwriting existing values in a slice, when you just want to get a sub-slice of it, but 2) to sort of make sure that you can keep on essentially adding to a new slice. I think it was adding to a slice without making the capacity too big. I think that was one of the cases, but it’s been a while, so I’d have to go check.
Yeah, pretty much in all the cases where you want to use it, the thing that you want to do is to say “If anybody appends to this slice, they should get a copy, and not keep using the same backing.” And there’s a function in the experimental generics slices package, which has actually been approved to be added to Go 1.21 next fall… There’s a function in there called slices.clip. And what that does is it basically - it sets the capacity of the slice to whatever the current length of the slice is, and so that way if anybody does append to it, they’ll get a copy instead of rewriting the existing one… And I feel like that’s so much clearer. If you’re reading some code and you see slices.clip, it’s like “Okay, that’s what they’re doing. They’re just like making sure nobody overwrites the end of the slice.” But if you see the three syntax version - yeah, it’s pretty weird.
I think I also don’t like that performance-wise, I feel like somebody could – I’m assuming that whenever you do like a slice and you set the capacity, that at that point if you go to append, it then has to copy to a new slice at that point. So like a new underlying array. So I think if people aren’t aware of sort of the performance impact, it could lead to weird code. Whereas if you do it, like you said, where you call slice.cap, I feel like it’s a little bit more logical to look at the docs and be like “Okay, this is actually going to have a performance impact if we do it a lot, or if we do it in the wrong way”, or something.
Have you got one, Carl?
Yeah, I’ve got a couple. One, I have an issue that’s open in the Go tracker… When did I open this issue? Does it say?
What’s the issue number.
So the issue number 21291, proposal: Go to remove bare return. And this says August 3rd, 2017. So it’s been a little while… I don’t think the Go team is rushing to implement this. But they haven’t closed it, so it could still happen; there’s still a chance.
Remove bear return… So in Go, there’s this idea of named returns, right? So a named return - what you can do is in the same way that you name your arguments that go into the function, you can name the arguments that come out of the function, especially when there’s more than one… But you could also do it if there’s just one. So with bare return, what you do is you could only use it when your function arguments are named, you can instead of like saying “return x, y”, just say “return”, and since it knows that x and y are the return values, it’ll just automatically return x and y.
[00:22:02.29] So I think that having named returns is really nice. Like, you can set it up, it’s part of the documentation, it helps for cases where you need to where you want to overwrite the return value in a defer, it’s good for that… But then the naked return –
It’s nice in interfaces sometimes to describe the outcoming arguments.
Yeah, if you have an interface, you can be really clear; or just anytime you’re returning two of the same type, like just make it clear “Okay, the first one is the min, and the second one is the max”, or whatever. But yeah, the bare return I feel like is just “You probably shouldn’t do it… You should just go ahead and be explicit about what you’re returning. You don’t need to just have the bare return keyword…”
So with that, do you find yourself using named return values less often, because you’re worried somebody might use a naked return?
No. I still end up using named return values a lot. But yeah, the bare returns I don’t use very often.
Yeah. I ask because I think it’s just a weird head game with myself, where I liked named returns, because they make a lot of code much, much clearer. But I weirdly avoid them sometimes, because I’m like “Well, I don’t want somebody to start using a naked return at this point.” And I don’t know why, because it’s not like I’m working in a codebase where I’m not going to see the code, and be like “Hey, let’s put a name [unintelligible 00:23:22.22] the actual return values here.” But just for whatever reason, I could see that being like a head game type thing.
Yeah. You could run into trouble with it, too. If you have like nested scopes… Let’s say you name the return err for error, and you’re like inside of a nested scope< and you say, “if error colon equals whatever, and error is not nil, then return.” Well, you don’t realize it, but you have created a new scope, and your error there, the error variable is a different one than the return variable. You’ve actually created a new scope, and you didn’t actually change the return variable… But now you do a bare return, and it returns the return variable and not the one – anyway, so you can run into these bugs like that. And probably, your linter, or some static typer, or something can catch that… But it’s just better not to have the issue at all.
Yeah, I do wonder, with some of these things, if we got together and just made a new linter that was just extremely opinionated about stuff like this… And it was just the most strict linter. Like, one that really gets in your kitchen; like, really gets up in your business. Because I’d have double vertical lines. If I ever see two vertical lines in code, that’s a pet peeve of mine.
Like an or statement?
Two empty lines.
Oh, I see. Two blank lines.
Two blank lines. That’s it. Yeah, I forgot what – blank… The word blank… My mind went into a state where I couldn’t remember the word…
What if you just want to tell somebody like “Hey, we’re really doing something different now”?
Yeah, I’d pop in a different function probably… Because it just looks like a mistake to me.
But you could do a comment, I guess…
Yeah, fill the space with a comment.
I’m just messing around. I don’t – I’m sure I probably have code with two lines, but I don’t think it’s ever really intentional.
Yeah, but you’d be fine if the linter was like “Ah, ah, ah, Jon… What are you doing…?”
Do you guys ever do those comments where you have like a lot of ASCII art in them, or like a bunch of stars, and it’s set off on all sides by stars, and if you ever have to change the comment, it’s really annoying, because it gets all uneven… I guess you could put your keyboard into like insert mode or whatever instead, so it doesn’t have a problem, but… Yeah, those are great. Those are terrible.
The only thing I’ll do is, with comments - like, if I want them to have like a max 80 character width, or something, you can set up like an extension in VS Code or whatever to just sort of break it up for you.
[00:25:59.02] Oh, that’s nice.
So I know that shortcut in my in VS Code for the extension that I have. But that’s the only one. And I don’t care as much – like, if I’m reading somebody else’s code and they have a really long comment, I just have word wrap on, so it doesn’t make a difference to me… But it’s whenever I’m recording videos that other people are going to watch, or doing other things like that, for whatever reason I’m kind of anal about that, I guess; I just want it to be broken up. But I don’t do like any other fancy stuff, because like you said, anytime you want to change anything, which to me feels like all the time, it’s just obnoxious to do.
Yeah, I think that’s a good practice - design for change. Like, stuff will change. I think we too often think of we’re building the final thing here, instead of remembering we’re building something for now, and we’ll probably change it at some point. So design for that. That’s partly why – I mentioned not using structs for field names. Anytime you can be very explicit in code, I always prefer that. One of the thing is I will never – I don’t use panics. Do you ever use panics in your projects?
I think it depends on what you’re doing.
So if I’m writing like a 100-line script to do something real quick, I don’t mind having panics in there. I’ve seen how you can set up a main function that calls a run function and returns an error, and I agree that that arguably could be just as easy. But sometimes I will find myself just throwing a panic in there and just not caring because of what it is. But if I’m building an actual application, I very, very rarely am using panic.
I find that panics I will use if it’s something where it’s like a programmatic error. It’s like, let’s say you have a function, and you’re supposed to take the number of things, and negative numbers don’t work… You could just use like a uint for it, but it’s usually better just to use a regular int and say, “If the number is less than one, or less than zero, then panic.” Things like that, where it’s like, because you’re being called programmatically, you know what the correct answer is.
In the standard library there’s regex.must, and template.must. There’s a couple of things in the standard library that are .must, and the idea is you have pre-coded these at compile, you know based on the static source code that this should be correct, and so you don’t really care about checking for an error. If there’s an error, then it means that you’ve created some sort of error, and the error can’t be recovered from; it’s a programming error.
I think a good example of this, which I think you sort of mentioned, Carl, was if you’re using a slice and you use an out of bound index, you always get a panic or a runtime error at that point. So if I was creating like a linked list type or something, and somebody tried to access something out of bounds, like, having that return an error every single time is kind of annoying to deal with in the code, when in reality, that’s somebody’s logical error in their code. It’s not really something you can recover from.
Yeah. I like the pattern of if it’s a message to the engineer, or to the developer, then a panic makes sense; if it’s to the person doing the programming. But in both of those examples though it’s possible that those values were coming from the user. So in a way, even if you err on the side of errors, even if it’s not coming from the user, you get used to that same mechanism of it bubbling. But yeah, I mean… The other time I will use a panic is if something’s going to panic anyway, and I want to sort of get in there before it, and sort of panic before it. It’s like in real life, if you’re in a situation with a friend, and it’s a bad situation, the first person to panic - like, the other person can’t also just panic. They have to be no like “No, calm down. Don’t worry.” You know what I mean? And then if you’re not ready to panicking, then you’re gonna be like “Okay, I’ve calmed down now”, and no one’s panicking. So it’s good trick there.
But if you’re calling a method and it’s gonna panic because it could be nil, something in there could be nil, I might check if it’s nil, and if it is a panic… Because it’s gonna panic anyway. Because sometimes it’s nice to avoid having that second error argument; you can build more composable things sometimes. So yeah, okay, I probably still won’t panic though much…
[00:30:18.29] I feel like if you see a case where a panic is okay - it’s rare, but I feel like when you see it and discuss it, it’s a little bit easier to be like “Okay, that makes sense in that one case. But don’t make a habit of it.”
I’ve seen some things where people are really opposed to panics, and they want there to be errors on things like slice out of bounds, and whatnot… And I just feel like that’s too far. If every single time you used a slice, you had to say [unintelligible 00:30:45.09] that’d be too much.
Yeah… Interesting. Yeah, you’re probably right. I probably agree with that. Okay, Jon, is there something else you avoid in Go?
So I’m assuming everybody - well, maybe not everybody… But I think, Mat, you’ve talked about this in the past… Do you think like labels, or anything that sort of reminds you of that Go [unintelligible 00:31:03.01] type stuff that you saw in like basic programming - I pretty much never use it, and I’m of the mindset that if I was teaching an introduction to Go course, I don’t think I’d ever bring it up. Because they might see it at some point, and then they could be like “Hey, what is this?” and that’s fine, but I don’t think I’d want to encourage people to use that. And I feel like when you’re showing them that stuff early on, they’re gonna want to start using it more… And I just – if I was teaching, for like somebody new to Go, I don’t think I’d even bring that up. I’d be like “When you see it, you’ll know it’s there. If you run into a case where you need it, you can figure it out. But for now, you don’t need to know about it.”
I think the main time when I end up wanting to use a label is if you have like a double-nested loop, or like a loop with a switch statement inside of it or something, and then you want to break out of the switch statement… But I agree that in most of those cases what you can do instead is like to just have a sub-function, and you return from the sub-function instead, and it’s much more clear than having to use a label.
Yeah, I agree. That’s how I always get around it. Because you can create little anonymous functions in your other functions, and they’re sometimes very nice to just kind of storytelling, explain what you’re doing, break things up… But you don’t have to necessarily build a type, and have state, and all that stuff. You can sort of have it all locally. I love stuff like that. And I use that actually for – I use http.HandlerFunc() a lot more than I use Http.Handler, even though – I think if I was doing a package that was part of like some middleware thing, it would probably be both. Like, you would provide both. But just in everyday usage, I prefer the handler func, because you can just do things with functions very easily, including middleware and things. You can just have a function that you call, that returns a new handler func, and you pass in the other one, things like that…
And having everything you need, even like sometimes request/response objects inside that one function - you do end up with a quite a meaty function, or plant-based, protein-packed function. So there’s a lot in there. But when you’re maintaining it, you’re like “Oh, what’s gonna happen? I’m gonna go to this endpoint and have a look.” And then you have everything you need in that endpoint. There’s no side quests. Marc Chipouras at Grafana, someone I work with, has this idea of you have to go off on side quests, and you get distracted. And you get to not do that if you keep everything together, and verbatim, and verbose, right up front.
Yeah. I think a lot of times in an application you can end up with – like, you could just do it with global state, right? There’s no reason that you couldn’t just say “Alright, I’ll have all my variables at the package level, as global state, because it’s just a single application, and it’s not a big deal…” But it’s better to use them as struct variables, just because it puts them in one place, and then you can see them. And it just conceptually makes it a lot easier to deal with if it’s like app.database versus database, right? If database is just floating around at the package level, you’re like “Where did that come from? What does that mean?” Versus if it’s app.database, you’re like “Oh, it came from the app.” Even if there’s only ever going to be one app, and you couldn’t have two at the same time.
[00:34:20.20] Yeah, I completely agree. I would actually do away with global state altogether. I get it for like – if you’re writing quick little scripts… That’s I think where it came from; it’s just very easy to start doing things. But if you just – just by being explicit about everything makes the code so much more readable. And think about testing. If you’ve just got one thing in global state, your test code often – if you’re going to try and sort of mock it or something, it’ll interfere with it in weird ways. And it’s sort of a bit too magic.
So yeah, I would say avoid global state altogether, and just have – I mean, obviously, variables and constants and stuff like that, but not state that’s like – errors, I would probably keep there, if they’re part of the package level, if it’s part of the API design. But other stuff, I’d probably pop it somewhere else.
Do you write much code that uses like templates, or like embedded files?
Yeah, I do.
Because one pattern I’ve seen is basically you’ll have a directory that you want to – maybe it’s your email templates, or something… And essentially, you’ll have one Go file in that package that basically just has a global state, like embed.fs type filesystem there, just so you can put it into your binary and have it there… So I think for things like that, and templates and stuff like that, that it’s very useful. I only mention this because I don’t want people to listen to them and be like “I should never use these”, because I definitely think there are cases where like, okay, it’s a lot easier to do this than to be like “Every time I start my app, I have to go tell it where all my email templates and everything else are, vs. like just import those as an embedded file system and use them.”
I think that the key is with the usages of global state that aren’t bad is they’re essentially constant. So some of those, you can’t actually use the const keyword, because for example the file store has to be an fs variable, and there’s no such thing as an fs constant. But once the application starts up, you’re not changing it, you’re not modifying it. And I think that’s pretty much a very clear, bright line of “If you’re going to have to modify it, then it probably shouldn’t live at the package level, if you’re modifying it after startup.”
I think that also extends to the use of init. I think init’s okay if you’re just using it to set up some basically constants, or just variables that need to be calculated, but they are unchangeable probably throughout the app. That’s it. But in a way, they’re kind of – that’s not really global state in the sense of like storing state there, that’s going to change. I think mutability of it might be quite important in this. It’d be quite nice if you could do the same things you can do in variables in constants, but they just work slightly differently. But you could do it with syntactic sugar probably, couldn’t you, Carl? Pop that PR out in the morning…
There was a really interesting proposal by this guy [unintelligible 00:37:18.14] Have you guys ever run into him online? He’s a super-genius about programming languages. Like, he’s this German, maybe Swiss-German guy who - he just understands the Go type system in this really intensely intricate way. And he had one blog post where he said “What we should do about package-level state is to have dynamic variables.” And ever since then, I’ve always like looked at it and thought “It should be a dynamic variable. Yeah.”
[00:37:46.29] So the way dynamic variables work - it was in some older languages, like Bash, and I think maybe in Perl if you don’t use the my keyword, it ends up being a dynamic variable… But basically, no programming languages that are around anymore use dynamic variables, because they stink. They’re totally unusable, except for this one use case. And the way that it works is when you call a function, and then you look at the global, what you see depends on who called it, and it’s like, it would paste over the value with the new value… Anyway, it’s this really crazy idea. If you google for it, I think you can find it; or we can put it in the show notes. But it’s – yeah, it’s this really crazy idea. And if there’s ever like a Go 2, I would be really interested to see an idea like that make it into the language. But definitely, it’s not something that’s going to change anytime soon.
So I think it was Dave Cheney that wrote the blog post about it, about making errors that are constants, but you have to basically make strings, and then… You essentially make a type that’s really a string underneath the hood, and then you add the error function to it. Do you guys find yourselves actually using that a lot in code, like to actually make sure it’s a constant error versus a variable error?
No, I don’t worry about it… If somebody’s screwing with the variables that should be constant, then they have their own problem.
Yeah… For the errors thing, for a while I got into having a function that would check. So you’d kind of inspect the error, even if it is like just checking to see if it’s that type. But yeah, I don’t know if I do that these days with the new error stuff.
I’ve done it in the past, and every time I’ve done it, I’ve just basically thought the same thing as Carl. Like, if somebody’s intentionally coming through here and trying to change these errors, they’re going to screw something else up, almost certainly.
What are they changing them to?
I’ve never seen somebody do it, so I think it’s one of those, like, “Oh, these can change, so somebody could do it.” And I’m like “Who just imports a package in their own package, and then changes the errors?” I’ve never seen that happen.
Counterpoint, Jon - let’s do it. Let’s do it so it happens.
Like, we have to get a popular enough package, that people are importing, and then they somehow often not realize that we’re changing one of the errors…
Because you could change it in a different package in an init.
Yeah. When you import Testify, make it so that it changes io.EOF to be something like really funny. Like, instead of saying “end of file”, it says like “You’ve been pranked by the prankster.”
King Prank. Kind of the Pranks.
“That’s what you get for not reading King Prank”, yeah. This would be a good YouTube prank. “YouTube Prank Challenge!”
So you’d need a major release just for that, and then you’d need another major release… Is that a breaking change? I assume that’s a breaking change.
Oh, it’ll break a few things… I’d say it’s a breaking change.
You’d be like “Nothing in my package changed. I don’t know what you’re talking about.”
[laughs] What would happen to me in the community? I’d just be shunned if I did something like that.
I think everybody would immediately be like “Was Mat hacked?” I think that’s probably what would happen.
No, I would not assume you were hacked. I would assume you had just done it.
Done it as a prank.
King of the Pranks…!
King of the Pranks. You did it for the upvotes.
Well, maybe that could be a regular section. That could be a new section of our show. Pranks. It’s Go Pranks… And we just do loads of Go pranks, like phone Dave Cheney and say, “Oh, hello, sir. Your pizza’s on its way.” And he’d be like “I didn’t order a pizza”, and we’d be like “Hah!! Go Pranks. We got you big-time, Dave!! Big-time!” And then hang up on him.
I don’t think that would work very well. I don’t know many people’s numbers… And he’d really stop [unintelligible 00:41:18.22] them to us if we started doing that.
And there are no genies!
That’s true, there’s no genies in that… I’ve got some other ideas for new sections of the show. “Cleaning your tech time.” We just spend ten minutes and everyone cleans their tech. Get some antiseptic wipes, clean your keyboards, clean your trackpads, your mouse… Give your workspace a nice clean. That’d be a nice section; we’d just play some music…
I think that’s the most important thing I learned at my first job, was – you know, I came to work every day and I just ate my lunch at my desk, and at the end of the year my laptop was completely filthy. It was just disgusting. And what I learned from that is never ever, ever, under any circumstances, eat in front of a keyboard, no matter what; even if there’s like a hostage situation, never. Just don’t do it.
[00:42:09.08] I just like move my keyboard off to the side, so I’m like “Alright, this is all my eating space.”
Hang on… Carl, have you ever read the news, and they said “Oh, there’s a hostage situation, and the gunman has made people eat their lunch in front of our keyboards. People are livid.” I’ve never heard that… I don’t know.
Yeah, that doesn’t happen in Britain, because you guys have good gun control laws. This country is terrible…
I mean, I could see it where like - there’s the hostage situation, and they bring in the pizza for all the hostages, and he’s like “No, you can’t leave this room. Just eat at your desk.”
Yeah. “Eat your desk.” And everyone’s like “This is the worst time I’ve ever been kidnapped, or held at gunpoint, because of this… Normally, at least I could have my lunch outside…!” Like, people saying it pissed off, trying to say it so the guards hear them still, but not quite…
So can I tell a story related to this?
Oh, related to this…?!
[laughs] It has to be unrelated, sorry…
No, related to like the cleaning your hardware.
Oh, yeah. Okay. [laughs]
So a few weeks ago a friend of mine had me come – he’s like “Hey, my video card’s having issues. Can you come help me figure out what’s going on?” So I go to his house, pull his computer apart, and it is just packed with like dust because he’s never cleaned it out. Didn’t really – I don’t think he thought about it, whatever. So I took the video card, and I guess one of the fans in it had like – I don’t know if stuff had packed up on it so it became uneven or what, but basically one of the fans was like wobbling around, so it like wasn’t cooling… And I think one of the fans was basically popping out, essentially. So I’m like, “Yeah, your video card’s probably overheating at this point, because that fan is just not doing anything.” So yeah, definitely take some time to clean your equipment, people.
Oh, yeah. Whoa… How many years do you think that was?
Oh, I think that computer is probably seven plus years.
Wow. And his video card –
I don’t think he built it. I think another friend helped him build it at one point, and he just doesn’t do a lot of hardware stuff. He’s done more lately, but he just didn’t really think about it or anything, and… It also went through a couple of different moves, so… Between all of that, it’s not really shocking, but… But yeah, it was just kind of funny getting in there and I’m like “Yeah, um, this is gonna be an issue…”
Ugh… Yeah, okay. Well, there you go. Tech cleaning time. [unintelligible 00:44:14.03] Is there anything else you’d do without?
So one of the things on my list of things to avoid in Go, which is kind of new, is this idea of useless uses of generics. So if you guys aren’t familiar, there’s a classic Unix Shell blog post called “Useless uses of cat.” And what it is is it’s just somebody having like a sort of humorous rant about when you’re using the command line shell, and you use the cat command, the concatenate command - in a lot of those cases you can just eliminate it and instead use shell redirection. And it’s like sort of an old Unix joke…
So useless uses of generics - this is a thing that I see a lot recently, where people make a generic function, but then that generic function calls a function that takes the any type, and so then the generics aren’t providing any type safety. So specifically, this happens a lot where people will write a thing that calls JSON.unmarshal, or JSON.marshall. And it’s like, JSON.marshal and unmarshal - they just take the any type. So you haven’t added any type safety, you’ve just added like this wrapper that’s generic for no reason.
Does it make him feel better?
I think it does. I think people – they see that genericness and they’re like “Oh, this is good. This is generic.” But then it’s like, but then you’re just calling the function that takes to any type anyway, so it’s not actually generic… [laughs]
So are you referring to cases where they’re like passing data in to turn it into JSON, and they’re not actually getting anything back?
[00:45:53.00] Yeah. Or somebody will be writing like an API handler, and they’ll say, “Okay, given type t, that I’m gonna call this data, and turn it into JSON, and send it over HTTP…” And it’s like, okay, but as soon as you take the data and you turn it into JSON, you’ve lost all the type safety. So you could just say - take the any type, and you don’t need generics for this. This is just like regular code that would have worked without generics.
Okay, so you’re not referring to cases – like, in my head, I’m thinking I could write a wrapper around sync.map that uses generics. And now all of a sudden I can like actually create a typed sync.map wrapper, essentially, that actually gives me back the types that are already in that type, and I don’t have to go through all those hoops of converting from any back to my type. But I feel like that’s very different than what you’re explaining.
Yeah, that’s a different case, because what you’re doing with the sync.map is you’re trying to make it so that all of the variables inside of the map have the same type. And so the fact that it has an any type is kind of hidden, or whatever… But then with like the JSON one, it’s not like you’re trying to get all the calls to the JSON function to have the same type… I don’t know. Or like all of the – yeah…
I think I get what you’re saying though. If you’re responding to a web request and you’re gonna call that and you’re not actually gonna return that same type to them, it doesn’t really make a difference.
Or even if you are going to return the same type, with generics a lot of times it’s better, just because of the way the type inference works, to do things like - if you pass it as an argument, then you don’t have to explicitly declare the types. But then once you’re passing things as arguments, then it’s like “Oh, you could just –” You know, the way that like JSON.unmarshal works, where you just pass like the pointer in and get the pointer out again. And that works, too. You don’t actually need a generic for that.
Okay, I think we’ve got time for one more… Jon, I heard a rumor; a little birdie told me you don’t use internal packages. Is that true?
Not don’t really, ever. I can’t think of a time that I’ve actually used it. But I think this is also like a byproduct of where you’re working, and that sort of stuff. And I think it’s a good example of like the type of feature where – like, generics are an example of this too, when they were coming, where a lot of people were like “I’ve never needed them. We shouldn’t have them in the language.” And I think they ignore the fact that there’s probably a whole subset of the Go community that could really benefit from them. And internal packages to me are like that, in the sense that if I was at a big company, with like a big mono repo, I could see internal packages being very useful. But since that’s not how I’m working right now, it’s just never been something I’ve needed to use, or even messed around with.
Yeah. Carl, do you use them in your day to day?
Yeah… Basically, everything I do, I open-source, unless there’s some reason I really can’t open-source it. And so for that, I do like to use the internal just sort of to mark like “This is the boundary of stuff that you can look at, and this is the boundary of stuff you can’t look at.” Like, I have a requests packages, and inside of that there’s an internal that does the testing stuff… And it’s like, okay, I don’t want you to start relying on requests.test; just use requests, and don’t mess with the tests stuff in there. But other than that, yeah…
So are you doing it so that you can still export the stuff and use it throughout your code, but you are basically telling anybody who’s going to consume that API or that library that, “Hey, you shouldn’t rely on this to stay constant, because it’s something that I might change”?
Yeah, it’s just whatever the internal details of that package are… But it’s not something that I want anybody else to really look at. I’ve put this online as open source, but just because everything is open source… But I don’t vouch for it in any way, and you shouldn’t use it.
So Jon, maybe because you don’t do enough open source, like you’re not such a hero, like Carl…
Well, maybe that’s the issue… Or maybe it’s just the way I’m testing stuff, too. It’s hard to say.
Yeah. But I think it’s right - if you’ve got a package, people will depend on anything they can get their grubby mitts on. So having internal stuff, you literally can’t import that, can you? What happens if you try and import an internal package, Carl?
I assume it gives you an error message. I don’t know. I don’t know what the error message is.
I’d assume the compiler would throw an error, but I don’t know…
I was expecting you to know the exact error message.
[00:50:11.27] Yeah, I should have memorized it before the show, but you guys told me at the last minute, so I didn’t go down my list of things.
Otherwise, you would have definitely checked.
When you use internal, Carl, do you use like nested internals, if that makes sense, where you could have like A, B, C as like different folders, and then internal, and then have more below that?
No, I only ever just have the one. Yeah, I know what you mean… Like, maybe I would like want to protect this from that, and that from the other thing… But no, I just only ever set up one, and it’s like whatever is in the internal is part of my project.
For some projects, that’s the one that threw me off a bit too, because it’s like you’re protecting your code from yourself, from like you importing your own stuff somewhere else… And with a big mono repo, with a big company, I
could see that. But if it’s me and one other person working on a small open source project, I’m like “Um, I don’t know if we really need this to do that.”
Yeah, you get a lot for free by having small little teams… Like, when you scale and have more people collaborating, it’s just the nature of it - it’s trade-offs, things are different. You do extra work to protect things like that, probably. I think you’re right. Yeah, okay… Well, it’s that time, dear listener… It’s your favorite time… It’s everyone’s favorite time… It’s time for Unpopular Opinions!
Do you use browsers to remember your passwords, fellas?
This is a trick. You’re trying to trick me into giving up operational security details. This is a long con fishing job, and you’ve just been buttering me up for episode after episode…
“Oh, Carl, your jokes are so funny. Come back on the show and tell me your mother’s maiden name.” I get it. I see where this is going.
Yeah… No, absolutely not, Carl. But what is your favorite six-digit verification code that you’ve had?
Oh, yeah, I think the favorite one I’ve ever had - that was probably the time that I got 123456. That was just great… I couldn’t believe it.
That would be amazing, if you ever got that. If it’s truly random, that’s as likely as any other combination… But I imagine not in that order; any other combination in an order, in a specific order. But it just feels special, doesn’t it? I once got mostly one – I got one once that was just zeros and ones. Like a little binary one. And I’ve never had so much fun verifying. I just got to type that in… Oh, I loved it.
No, I’m thinking like - browsers, sometimes it’ll say… Well, I use a password manager, so I use a thing where I put all my passwords in one thing. So if that gets hacked, then that’s end. But the assumption is that’s quite safe… But sometimes also browsers will remember passwords. It’ll say like “Oh, should I remember this?” I’m like “Yeah, sure. Just remember it. That’d be great.” And then sometimes it’ll change for some reason, because you have to go through a different device; you change your password, and then you come back, and it’s remembered your previous password… Which is fine the first time; you can’t expect it to read minds, yet… But I then change the password, and submit through successfully, and it doesn’t update. It doesn’t update its memory; it doesn’t update the password. And then every time I come through that same route, it remembers the old one. And that’s my unpopular opinion… Browsers shouldn’t do that. They should update, or they should go away. Any objections?
This was different than I expected, is how I put it.
It’s different than how anyone unexpected…
[00:53:56.02] I will say that what you’re describing is exactly why I don’t store any passwords in Chrome anymore. I just felt like it got so annoying that it would constantly have the wrong password for different things… So I just use one external tool that I can have on all my devices. And it actually frustrates me when Chrome tries to suggest something, because I’m like “I don’t use your password thing anymore. I thought I disabled this… Stop trying to do stuff.”
Yeah. But why don’t you just use one password instead. Then it would remember it right, won’t it?
So I use 1Password, and that’s where all my stuff is. So I’m saying, like, whenever I had Chrome – I don’t care if you know which one I use. But whenever I was using Chrome stuff, I feel like I had them stored in both; like you said, you’d try to update, it wouldn’t update… It was just frustrating, so I just disabled it.
So I almost feel like the browsers just – on one hand, I like that they do it, because there’s a whole class of people who are never going to use like 1Password, LastPass, or any of those types of software. But at the same time, it leads to cases like that, which I think might turn people off of it… Especially if you’ve got somebody who runs on an iPhone, running Safari, and then they go jump on a computer and run Chrome, or something.
I’ve run into things were my passwords on my iPad weren’t syncing to my iPhone, and I didn’t realize this for like some number of months… It was like, the iPad was just becoming –
Yeah. Well, because how often do you have a new iPad password? I only ever use the iPad to browse in bed. Like, before I go to bed I’ll read Hacker News… And then I have Hacker News set up – there’s like an anti-procrastination feature in Hacker News, where you can tell it to disable itself after a certain amount of time… And a message comes up and it says, “Go back to work. You’ve set your –”, and it’s like “No, it’s not go back to work. It’s go back to doing more entertaining entertainment options.” Like, that’s why it’s there, is because otherwise I’ll just spend the whole night reading comments, instead of like doing things that are actually fun…
But yeah, so my iPad, for some number of months, it was like all of the passwords I made on it just got stuck in this island, and never got out, and I had to reset all those. It was very annoying.
Now, that does sound annoying. But you got through it.
I got through it. I survived. I’m the survivor, just like that time they took me hostage and made me eat a pizza at my desk…
Yeah… They will do that. Yeah… No, my startup idea was basically - you know 1Password. It’s good password security. And same thing, but two passwords; twice a secure. You just put two passwords in to get through that first thing, and then you can have all your passwords.
I think you’re not going to be able to sell it. I think the way that you sell it is half a password. Go smaller. Zero passwords. Negative one passwords.
“We tell you the password, and you tell us if that’s okay.” That’s how you sign in. I like it.
Yeah, you tell us if we sent you the right password or not. You could do a multiple choice. We’ll send you three passwords and you click on the one that’s real.
Isn’t that how certain background checks work for stuff? When you’re going through it or whatever, it’s like “Which of these addresses did you live at?”
Oh, I hate those, because –
It’s like, this is not top secret information.
Well, one, it’s not top secret information, and then two, it’s like wrong. It’ll give me information that’s like tangled up with my spouse or something, and it’s like “I’ve never lived in that city. I don’t know what they’re talking about. Maybe my spouse lived there 20 years ago… Who knows?”
But you have to lie then, because you think you know what it’s getting at…
Yeah, you have to guess what it is. It’s like “I don’t know…”
Even if it’s wrong. I think you’re saying “Oh yeah, but gets it wrong.” But it’s supposed to, isn’t it? Two of them are supposed to be wrong, Carl. You’re like “I didn’t live at these three addresses. These two are wrong.” But that’s the point of the check.
[laughs] No, all three are wrong. Sometimes all three are wrong.
Oh, yeah, all three are wrong. You know, it’s like when it says “Click all the bicycles” and there’s a picture of a dog. And it’s like “How do I get past?” And then you take some couple of hours, and then you think I can just click Verify now, or whatever. It was a trick one… And it worked. I can’t believe it. That’s when I got my zeros and ones verification code though, so that cheered me up after…
Well, dear listener, I’m afraid that’s all the time we have today. I hope you enjoyed this episode of Go Time. If you did, please tell all your friends about it. If you didn’t, tell your enemies; either way, let’s talk about it, and then you can tell them about Carl, and then they’ll say, “What?! No… That’s not real.” And you say “No, listen”, and they’ll put it on and then they get to meet Carl in real life… So that’d be lovely for them.
Well, that’s it. Thanks, Carl, for coming back. I really appreciate it. Great to see you as usual.
Mat, I love that you needed somebody to be on a kind of wacky episode, where we just sort of goof around, and you thought of me, so… Thanks.
[laughs] Yup. And of course, Jon Calhoun.
I don’t think you thought of me. Let’s not pretend here… I’m pretty sure Jerod just forced me in here.
He needed somebody to be the straight man.
He’s like “Look, I can’t let Mat go on his own. We’ve got to put somebody there.”
There’s gotta be some adult supervision…
Okay, well, I’m afraid that is all the time we have. We’ll see you next time on Go Time. Bye, everybody! Bye!!
Our transcripts are open source on GitHub. Improvements are welcome. 💚