Transcript from the "Explicit Coercion" Lesson
[00:00:29] Obviously we have to statically cast from one type to another to do any kind of reasonable work in our programs. You have a number that you receive from an API call, and you need to print it on the screen, well guess what, it has to be converted to a string so it can be printed to the screen.
[00:00:45] You receive a string as input in the input box, and then you need to do some math with that. Guess what, it has to be converted to a number. I would go almost so far as to say there is no non trivial program in existence, that doesn't have some notion of some type conversion happening.
[00:01:28] And I'm gonna arbitrarily divide this into two categories that I'll call explicit and implicit. Immediately upon saying that, most of you probably have some notion of well explicit coercion is okay, it's implicit coercion that's bad. So typically developers will have a perception that explicit mechanisms in code are preferable, where implicit mechanisms are easier to misunderstand, and not fully grasp or be able to tell that they're happening in code.
[00:02:00] So typically we'll label the explicit as good, and implicit as bad. I wanna set aside some of those notions, because I do not mean to put these in two categories and say explicit good implicit bad. As a matter of fact, I think the most benefit, the biggest proposition benefit here is implicit coercion.
[00:02:22] But we need to first look at these as distinctly different approaches to the problem, and then ferret out what parts of the explicit and implicit coercions are useful, and what parts will not be useful to us. And by the way, this is not a scientific thing, like you must do it this way.
[00:02:38] There are things that we'll talk about in this course, which is like, there's no argument. Like lexical scope works this way, there's no subjectivity, there's a lot of subjectivity here. But there's a cargo cult against coercion. [COUGH] And you've heard plenty of those cases for why people don't wanna do that, and why you should never use the double equals and all of that stuff.
[00:02:56] And what I wanna take you through is the distillation of the case in favor of these mechanisms. So set aside if you would while we go for this next little bit, set aside some of you preconceptions about what's bad about these things and why we should avoid them.
[00:03:11] And I only use type script, I only use static types of whatever, just set some of that aside, because I really do genuinely believe that some of these mechanisms can aid us in writing better code. All right, so let's look at explicit. My basic what you need to know sort of definition for explicit is that it's obvious from the code that you're doing it.
[00:03:48] But it's still an explicit mechanism, you can see in the code you're going from a string to a number, it's very clear, okay? But even within that statement, there is some subjectivity, which we'll see here on the very next slide, okay? So we're going to talk first about the case of going between strings and numbers.
[00:04:06] It's one of the most common cases where conversion between types will happen. So let's talk about going in either direction between string and number. Let's imagine we start out with a string quote 123, and what we'd like to do is convert that to the number 123 we'd want to coerce it, okay?
[00:04:23] Multiple ways to do that coercion. One way that is often cited, but which I will say is not actually coercion is the parseInt function like we see on line 2. Now with the parseInt function, it's an unusual occurrence that its name is actually very descriptive of what it's about.
[00:04:58] It says right on the label what I do is parse integers. So think to yourself what is parsing, and what is the relationship between parsing and coercing? If I coerce a number out a string what's the difference between that and parsing a number out of a string? Well I would argue that these are distinctly different concepts, and using the parseInt function to coerce is a misuse of that mechanism, and it should not be surprising that may do things differently than what you might have otherwise expected.
[00:05:30] So parsing is taking a string going from left to right, looking for characters that match what you're expecting, in this case integer numbers like the decimal 0 through 9, matching those, and then producing the number equivalent. And stopping as soon as you no longer see characters that represent that number.
[00:05:48] As soon as you find the first unrecognized character, stop. That's what parsing is about. So in CSS you have a property that is 123px and you pull that property out so you have the string, quote 123px. If you parse that string for an integer, you're gonna get the integer 123 out because it's gonna start from left to right.
[00:06:08] It's gonna see the 1, 2, and 3, recognize those, parse those out. As soon as it sees the p we're in base 10 p is not a valid character, so it's gonna skip over, I mean it's gonna stop at that point, okay that's parsing. Coercion on the other hand is an all or nothing situation, the value you give me either has to completely coerce to this other type, or it fails entirely.
[00:06:33] There's no partial coercion, so it's not like parsing. If you give me 123px as a string, and you asked me to coerce that to a number, there's only one sensible answer for that, and that's the NaN value. Because 123px is not a valid number, make sense? So parsing is not coercion.
[00:06:53] I would advise you not to try to use parsing for coercion, or to try to use coercion for parsing. Decide what you need, and more importantly, use the right tool for the right task, because you wanna communicate clearly in your code. There is not only use the right tool for the right task, but use the right tool in the right way.
[00:07:13] I use the silly metaphor, you know how people say well don't go around swinging a hammer if you're trying to drive in screws, right? That's a very common metaphor. But let's say I've got a bunch of nails, and I wanna drive those nails in I have the correct tool, I have the hammer in hand cuz I have the right tool.
[00:07:28] But if I put that hammer in my mouth and I start swinging, using the right tool, right? But I'm not using the right tool the correct way. So, both of those are important in the equation, use the correct tool and use it correctly, as designed. So, parseInt, it's designed for parsing and we should parse with that.
[00:07:48] [COUGH] Then we turn our attention to line 5. You'll notice I'm using the number, that native function, remember those we talked about earlier. The native function When we don't use the new keyword in front of it, when it's just the function call, it takes whatever we give it and it coerces it to that corresponding primitive type.
[00:08:05] In this case, that primitive number. You will notice that line 5 actually kinda looks a little bit like a static type cast in some languages. Technically, here it's actually a function call, it's not a type cast. But it sort of looks the same. And I don't think that's an accident, I actually think that's a good thing for us to draw upon the precedent if people are used to seeing explicit type casting in a static type oriented language.
[00:08:34] We can do something very familiar and recognizable to them by doing a coercion from a string, in this case, to a number, using the number function. So I would recommend and endorse that, as a way to do explicit coercions from any value to a number, but in this case, from a string to a number.
[00:08:52] Anything you pass into the number function, it'll coerce it to its number equivalent. Including NaN if that's the appropriate coercion, okay? Now, [COUGH] having said that, very few places have I ever seen in any code base, especially in open source, anybody using the number function. It seems virtually every time that people want to explicitly coerce to a number, what they instead use is this unary + operator, as you see on line 8.
[00:09:20] Now what's that operator about? If you read the spec, that operator, specifically takes whatever you give it, take the operand and runs the two number abstract operation against it. It does the exact same thing as the number function, exactly the same. One's an operator, one's a function call.
[00:09:39] So both of these could rightly be under this heading of explicit coercions cuz they both explicitly do one and only one thing. There is no other purpose to that unary plus operator than to coerce to a number. And many people in the opensource community, if they're doing anything in their code base where they need to coerce to a number, they'll use the plus operator.
[00:10:00] That can get a bit confusing when it's like x plus plus y, where the second plus is separated with a space, so now it's not the plus plus operator but it's the you can get really confusing with it, okay? But until I just taught you that, there is at least some of you who have probably never seen that or didn't know what it was doing.
[00:10:20] So is that an explicit coercion for you? Well, until you have learned it, it may not be as self intuitive as, for example, the number function. I think most people seeing line 5 not even knowing what is going on, would at least have some intuition, you're trying to make a number out of something.
[00:10:59] For the members of your team, you have to decide what is explicit and what is not. Just like you have discussions or should have discussions about your style guide, you should have discussions about when we want to coerce from value to a number, how do wanna do it?
[00:11:16] We wanna have a consistency. Why is the style guide even important? I mean, is it just so that we don't have to argue about spaces versus tabs or trailing commas, or any of that other stuff? Why are style guides important? It's the same reason why this decision would be important.
[00:11:32] As a matter of fact, I'd argue this is a really important part of a style guide. Style guides are not just about literal code style, they're about idiomatic style as well. Given a certain type of problem, how do we always choose to default to solving that problem? In this case, given the problem of wanting to convert from one type to another, how do we do that?
[00:11:53] Why is consistency so important? Because when you are consistent, it makes bugs more obvious. When you're inconsistent, bugs have a much easier way of slipping through. So I would recommend, strongly, that you and your team members have a discussion about, hey, here's this thing that we know we're gonna have to do, let's stop pretending that coercion doesn't need to happen.
[00:12:17] Let's stop pretending that coercion is all bad all the time, and let's talk specifically about how we didn't wanna do it when we need to do it. Let's be intentional about it. Choose the number function, choose the plus operator. You might even choose to mix the two, although I would argue if you have some inconsistency there or if it's not a clear rule as to when to use one or the other, you've shot yourself in the foot, okay?
[00:12:41] But both of them would fall under this heading of the whole purpose of that statement is to do the conversion, to do the coercion, so they're explicit. There's no side effects happening. Let's go the other direction from number to string. On line 11 we start with the number 456, line 12 we call the toString method.
[00:12:59] You've seen toString before, we talked about it a little while ago. I think toString is pretty explicit. It says what it's gonna do and it does it faithfully, it creates a string representation of the thing that was called against, okay? Now, many people like to use that method because that method feels kind of Java ask to them.
[00:13:50] Because we are in fact explicitly calling a method that seems to be named very properly to tell us what we want it to do, but under the covers, there's something very much implicit happening. You cannot call a method against a primitive value. That doesn't even make any sense if you really think about it.
[00:14:58] The fact that I can call length against the string primitive, for example. Instead of having to cast it to a string object and then call dot account or something weird like that. And just call .length on a string primitive. That's, in my opinion, useful mechanism. But that's an implicit mechanism.
[00:15:15] That's not explicit. There's an implicit coercion from the primitive to the native, happening. From the primitive literal value, to its wrapped object counterpart. So this is implicitly explicit. By contrast, line 15, pretty straightforward. The string function does exactly what it says on the label. You give it anything it will call the toStrings abstract operation under the covers and produce the string of it.
[00:16:14] They're not my favorite, but I would endorse any one of these as valid ways to get this result, okay? Questions so far about explicitly going between string and number? Does that make sense?
>> Kyle Simpson: What about going from any value type to the Boolean value type? This is a very common thing.
[00:16:38] Whenever we want to ask something, like for example, do a Boolean test like an if statement or something, and we're testing something that's not already a Boolean, we need a Boolean so we need to explicitly create a Boolean for that purpose. Another example, say you're producing an API data structure.
[00:16:56] You're gonna send it off to some API call and that API expect to have true or false in some property. But the thing you have is like a string or whatever If you just pass along the string you're probably gonna get an error from the API, especially if you're calling an API on a language that's more static typed.
[00:17:14] They say no, no, we need a Boolean. You have to send a Boolean. So what are you gonna do? You're gonna need to explicitly create a Boolean. Follow me?
>> Kyle Simpson: So start out with a string 123. You wanna produce the Boolean equivalent of that value, you call the Boolean function, capital Boolean.
>> Kyle Simpson: Same as with string and number, it's very explicit, it says exactly on the cover what it's gonna do. I've never once, in a real code base, seen somebody do that. But they ought to, cuz it's very clear and explicit. And if our goal is to communicate clearly and explicitly, why don't we spend a few extra characters doing that?
[00:17:56] Maybe it's because a lot of us have optimized for something different than readability. Maybe we've optimized for how quickly we can just bang out code. Instead of optimizing for the reading, which is where we spend most of our time anyway. So I think you ought to spend those extra characters.
[00:18:10] But you know how most people do it, line 15, that double negate. As a matter of fact, that's become so idiomatic that people actually believe that !!, on line 5 is its own operator. It's not, but a lot of people think of it as a single operator cuz it happens so often.
[00:18:30] I don't think I've ever seen anybody do the Boolean function in actual production code, but I've seen thousands and thousands of occurrences of that double negate. What's happening with double negate? Well, it's actually two separate operations because there is really just a single exclamation, a single negate operator.
[00:18:51] But here is what the negate operator does. It specifically takes whatever you pass it and if it's not already a boolean, guess what it does. It coerces it to a Boolean calling the two Boolean abstract operation exactly as you would expect. And then it does this other thing which is frankly a little bit annoying in our particular situation because it's converted to a Boolean then it flips the parity.
[00:19:19] So once it produces the Boolean then it says if you produce two I'm wanna flip it to false to produce false and it's true because it is the negate operator, right? So it needs to negate, it needs to not just coerce. So this is an operator that both coerces and negates.
[00:19:36] The coerced part we like. The negate part we, in this case, don't like. So what do we do? Flip it back with another negate. That's what the second exclamation is. We undo that undesired effect of flipping the parody. That's what's happening. Now that is explicit in the same way that the unary plus is explicit.
[00:19:59] If you know what negate does, you know that it coerces, it does have this other behavior too, but it's not like this is some side effect. It's specifically designed to coerce. So you could call this an explicit coercion. As I said virtually all occurences that I've seen use the double the gain.
[00:20:18] Before I taught you what it was doing, I bet many of you would have failed the interview question, explain to me exactly how double negate works. It's not a criticism against you, it's just most people never think about it. Well that makes a boolean, I don't care how it works.
[00:20:31] I do care how it works, and I'm hoping to inspire in you that you should care how it works cause it matters.
>> Kyle Simpson: Let me stop for just a moment. I have lots of little sayings that I pepper throughout, and I kind of will style these as, getify as laws or whatever.
[00:20:50] It's just made up truisms of myself. And they're not even entirely original thoughts of my own. But I would say this, code that you do not understand is code that you cannot trust.
>> Kyle Simpson: Code that you don't understand is code that you cannot trust. How much of your code do you actually understand?
[00:21:13] How much of it do you just use because that's just the way it's always been done? I just saw it on a blog post and I just started doing it and it just works, right? I'm just raising the bar and saying. If you wanna be good at your skill and your craft, it's not magical.
[00:21:52] That's the only difference. And you can do that, too. Question?
>> Speaker 2: Just asking if you could go over that double negate?
>> Kyle Simpson: Yep.
>> Speaker 2: One more time.
>> Kyle Simpson: The first one, the right most one on line 5, the one that's right next to the foo. That's the first one that's gonna run.
[00:22:06] What it's going to do is two things. First off if you pass it something that's not already a Boolean it's going to call the underlying two Boolean coercion which is essentially that look up table that we talked about. And it's gonna produce a real boolean. So here we gave it the string 123.
[00:22:22] Is the string 123 in the falsy table or the truthy table?
>> Kyle Simpson: Why is it on the truthy?
>> Speaker 2: Cuz it's not false.
>> Kyle Simpson: Cuz it's not one of the falsy ones that is explicitly listed, right? So if it's not falsy, it must be truthy, so it's going to produce true.
[00:22:41] Now it says, great, thanks. Now I have a Boolean, my job is to negate, my job is to flip the parity. So since you gave me a true, I'm gonna flip it to false. If you had given me a false, I would have flipped it to true. So we have negated, that's the first exclamation.
[00:23:15] Theoretically, they could not do the flipping. They're like well you're just gonna flip it back, I'm just gonna skip the flipping part. Maybe they do, maybe they don't. I don't know, I'm not an engine developer. But there is an advantage to using syntax, cuz syntax is declared if an engine's can more statically analyze syntax
[00:24:17] That's what you do in Java to try to come up with a true false. That's how you explicitly coerce, if you will, to the boolean. You just do this ternary thing and you end up with true or false. There's no question that the end result of that is either true or false, and we'd all probably agree that's an explicit mechanism then, right?
[00:24:36] But I call it explicitly implicit, because hidden underneath the cover is actually not so hidden at all. Right there at the very beginning of that statement, we have to do a boolean coercion on foo to do the test, to decide whether true or false is the result. You see what I'm saying?
[00:24:54] So we have to first do an implicit coercion to bullion to do the test. And then we result in the explicit true or false.
>> Kyle Simpson: Now that's just needless going, end a rounding ourselves, that's chasing our own tail if you will. Don't do it that way. Use line 2 or line 5, there are much better options to explicitly create a boolean, right?