 My name's Craig Buchek, and I'm gonna talk to you about Booleans today. If you wanna follow along, I've got slides up here in the lower right corner. You can get to it if you need to get to it later. I do have some references and some more details, things I won't talk about in the presenter notes. You can just hit P to toggle those if you're playing along or playing along later. My Twitter's in the upper right corner if you wanna tweet at me or about me. I live in St. Louis, and that means I get to go to a conference called Strange Loop without having to pay for any travel. And I went last year, and it's a great conference. Lots of things over your head there though, but it's really inspirational. My favorite talk was actually the pre-conference day at ElmConf. Elm is a programming language that compiles down to JavaScript, so I went to that. And it was a talk on Booleans by Jeremy Fairbank. And that talk actually inspired me to create this talk. I thought, how is he gonna talk 40 minutes about Booleans? Turns out that's really hard. My talk's probably gonna be closer to 30 minutes than 40. So that talk really inspired me. It was my favorite talk. But Ruby's very different than Elm, so this talk is gonna be quite a bit different than Jeremy's, but I'd recommend watching Jeremy's as well. So on to Ruby. Booleans are pretty simple, right? Things are either true or false. True and false are instances of the true class and the false class. See there? In fact, there's really only one instance of each. No matter how we get to true, it's always the same object. If you use the object ID method there, you get 20 for all truths. All falses happen to be zero. Oddly, you can't create a new instance of these classes, which kind of makes sense, because if there's only one of them, you don't wanna create a second one of each of those. So you get no method error on true class new or false class new. Interestingly, there's no Boolean class in Ruby. Try to pull up Boolean and it doesn't know what that is. If you look at true class, it has no ancestors that are Boolean. Just object, kernel, and basic object, just pretty much any object's gonna have those same ancestors. Apparently this is due to Ruby's small talk heritage. I suppose it's because it's dynamically typed. You'd never have a reason to declare a variable as a Boolean. So anywhere Ruby expects a Boolean, you can actually use any object. So you can basically, if you wanna treat something true, you can pass one, two, three, or a new object. There's only two things that are treated as false. Those are false and nil. You see the bottom two there. You might hear the terms truthy and falsy. They express something that Ruby will interpret as true or false in a Boolean context. You might come across those terms in our spec test. Expect one, two, three to be truthy, and you might expect something that returns false or nil to be falsy. I wouldn't recommend doing that in if statements though. You're better off to use something idiomatic, something intention revealing that returns a Boolean. Here, we're checking to see if the string is empty or blank and if the variable's a nil. This isn't really as big a problem in Ruby as in other languages. Like Perl, PHP, JavaScript interpret the empty string and zero is false. I think that leads to a lot of errors. I think that's probably the second largest programming language mistake ever made. There's something called the billion dollar mistake that Tony Hoare made. He said when he introduced the null pointers, he thinks that led to over a billion dollars of damage to the industry. So next we're gonna talk about Booleans used as parameters in methods. So I spend a lot of time in the Rails console or IRB or in Pry when I'm debugging some tests or whatever. I often all wanna see the class of an object. So you can just do objectName.class. And often all wanna see what methods that object responds to. Now you use a method that's on objectNameMethods or a method that's on classNameInstanceMethods. And you can see I'm using the same, those two methods up there. They return the same thing. Notice there's usually a return a long list of methods. Here they're running off the right side of the screen. Show of hands, who's familiar with either of these methods? All right, those with your hands up, do you know they take an optional parameter? A lot fewer. Like three, I think. And those of you who remain with your hands up, how many remember whether you're supposed to pass true or false to not show the methods of the superclass? One hand left. It happens to be false for these methods, but I can never remember that. I always have to look at the documentation. We're more likely I just trial and error and see which one it, what it takes. So to use it properly, you have to remember, do I pass it true or do I pass it false? So is there a way we could do better? Now, those are built-in methods and so this is sort of just a theoretical thing. The best way to fix this API is to use a name parameter to describe the parameter. It'd be nice if I could just say, instead of methods false, I could say methods superclass methods false to not show the superclass methods. But this method predates name parameters in Ruby and we need to keep backwards compatibility. So in older Ruby versions, we used to have to use an options hash to emulate name parameters. And since we wanted this to be backwards compatible, I would still have to do that. Also to take either a bare Boolean or a hash would have to use this method. So actually wrote a replacement methods method that gives us the option to either use the old way, passing false, or to use that named superclass methods option. Maybe I'll submit that to the Ruby core team. But ideally it'd probably be better just to have two separate methods. One called methods that gives you just the immediate methods and one called all methods that gives you everything. So how would you describe what that original method does? I would say that it shows the methods defined for this object or the methods only defined by its immediate class. Anytime you have an or in a description of a method or class, that's a code smell and you're probably violating the single responsibility principle. Came across this example from Rails a couple months ago when I was upgrading to Rails five. So these both do the same thing. The first one was the original Rails API, I don't know how far back it goes. So users an object and it has an association called things. Normally you just say user.things and it'll give you all the things that user owns. But if you wanted to reload, you pass true to things. Second is the current API. User.things.reload, it's more explicit. Again, what does that true mean there? The original API was deprecated as of Rails five and removed as of Rails five one. So not only is the new one clear, but the old way can lead to some very subtle bugs. So I found this bug. This is Rails issue 26,413. I actually have a link to that if you wanna check it out. So the bug report complains that the sales are being reloaded. And he's like, why are the sales being reloaded here? Anyone see the issue here? All right, so the problem is that the sales association doesn't actually take a hash, it takes a Boolean like the previous screen. So the true there. So as I said before, anything besides nil and false are treated as true. So that limit 10, which de-sugars to a hash is interpreted as true. So it's just client sales true. And so it's reloading the sales and it's not doing any limiting. You'd have to do sales.limit parentheses 10 to get it to work. Or maybe you could do where, I don't think you can do a where with limit. So very subtle, you can't really see why that's a bug. So I wanna take a short detour here to talk about something called Knasens. Knasens was a word that existed before computers were around. These are some definitions from Webster's dictionary as far back as 1913. Knasens means common birth or production of multiple things at the same time or the act of growing together. So 1992, a person named May Lear Page-Jones brought this idea to the object-oriented program community. First it was in a paper comparing techniques by means of encapsulation in Knasens. Then he wrote a good book or a book that I've heard is pretty good, I'm gonna have to pick it up sometime. 1996, what every programmer should know about object-oriented design. And then he had a follow-up when UML was all the rage in 1999, fundamentals of object-oriented design in UML. So he talked about Knasens. I've got links to those in the presentation notes, including the full text of the original article from the ACM. There's a couple other sites. Knasens.io is pretty good about explaining this pretty well too. So here's May Lear's definition of Knasens. It's a measurement of the amount of coupling or dependencies among components within a software system. And in particular, he's talking about an object-oriented system. So 2009, Jim Wyrick brought this idea of Knasens to the Ruby community. Unfortunately, Jim passed away a few years ago. He has definitely missed it conferences. So here's roughly Jim's definition of Knasens. Two pieces of code share Knasens when changing one requires a corresponding change to the other. And if you have excessive Knasens, it means the system's hard to change and hard to maintain. So Jim gave a few different talks on this. The first one was called the Grand Unified Theory of Software Design. He had an example very similar to my methods example. And in 2012, he had a newer talk called Knasens Conceiment. So Jim made the argument that Knasens underlines many of the other rules of good object-oriented design like dry and code smells and race conditions. So we want to reduce Knasens like we want to reduce coupling. Here's a list of different types of Knasens. They're ordered from weakest to strongest so we should prefer the ones towards the top. These are the static Knasens types. There's some dynamic types as well but they're all stronger than these so you'd prefer to use the static versus the dynamic. So I'll go through each of these. Knasens of name means agreeing on the name of something in two different parts of your code. So an example, anytime we call a method, the method name that we used to call it has to be the same as the method name that we used to define it. And it's the same with any variable or constant when we reference it, it has to be the same name. That's pretty common, right? Knasens of type is agreement on the type of something. Now we don't have static types in Ruby. We have what's called duck typing which means does it quack like a duck? If it quacks like a duck, then it's acting like a duck is close enough. Knasens of meaning is agreement on the meaning of the interpretation of specific values like true or false. Knasens of position is agreement on the order of values like if you have a method that takes three arguments, the order that you specify them is important. Although if you use name parameters, we go up to Knasens of name. So according to this, you should always use name parameters. I don't know if that makes sense but I'm actually thinking about that. Knasens of algorithms, probably one of the more interesting ones on this list, agreeing on a particular algorithm. And the two examples that I came up with are encoding and decoding passwords. You have to have, if you've got two different apps using the same database with encoded passwords, you have to use the same algorithm. The other one is credit card numbers. There's a checksum to make sure that your credit card number is a valid credit card number. And so you might have client side code, server side code and then a service that all have to run that same algorithm to run checksum on the card number. So remember, we always wanna reduce Knasens. If we can replace a stronger form with a weaker form, we've reduced Knasens. One way to do that is increased locality. So if you've got two things in the same file that have the same name or the same meaning, it's easier to understand than if it's in a different file or in a different program like that credit card example. There are different computers written in different programming languages. So note that Knasens of name is weaker than Knasens of meaning. And that's what we did here. We replaced the Boolean parameter with a specific meaning with a name parameter. And that specific meaning is difficult to remember. And we reduced Knasens by replacing Knasens of meaning with Knasens of name. So next I wanna talk about Booleans used to represent application state. That's a picture of the Balkan states, by the way. Let's say we have an editor class and it has several Booleans representing possible states. We might need to keep track of whether the user's editing. If the file is being saved or if there's an error condition. Anyone see a problem with this class? All right, the problem is we can end up in a combination of states that make no sense. What would it mean to be both editing and saving? Well, if there is true, then do the other fields actually make any sense? We should try to ensure our code can never get into an impossible state. There's a great talk by Richard Feldman. Again, it's in Elm, but it's probably still worth watching called making impossible states impossible. So how can we improve on that? We should use a single field to represent the state. This doesn't look like a big improvement. We still have the case statement there, but it does prevent us from ever getting in a state that's meaningless or invalid and ensures the options. And once in our case statement, the order of those options doesn't really matter in a case statement. We can do better than that though. Active record enum. Is something was added I think rails four? Maybe five, I don't know, I think it was four, to define possible states. This allows us to catch bugs easier because Ruby will catch an incorrect method name more easily than a mistype symbol or string. If you see here, we've got state.editing question mark. So any of the states that we define up there, editing, saving, and error, we get methods with question marks in there that we can just check them. There's several state machine gems. We could implement our own to create a state object. The gems usually have some other nice features that are helpful. Although the enum is pretty nice, it came out after the state gems. If you can use enum, I would recommend that first and then look into some of the state machine gems. The state class is pretty similar to the enum here in this example, except if we have a sufficiently specialized state class, maybe we can just delegate to the object itself and have it render so a state could do the render for us. The original version of that code contains a code smell called primitive obsession. It's using a primitive type when a more specialized type would be more appropriate. Usually that's gonna be a class in Ruby, of course. So one example is using floating point numbers to represent money. Another is using a string to represent a URL. If you think about it, a URL has more than just a string of text, it actually has pieces. It has the name of the web server, it has a path, it's got fragments, it's got a query string. So using a string isn't always the best idea there. So in Ruby, we're apt to actually abuse strings in this way. It's often called stringly type to play on words from strongly typed from other languages. But this is an example where we overuse booleans. This version still has primitive obsession. We just replace boolean primitives with symbol primitives. Next, I'm gonna talk about boolean fields. We've got a boolean attribute here to keep track of whether the object has been deleted. I used a little tool there I wrote called Vertis Active Record so I can show you in the code what attributes the model has. So we've got a deleted field that's a boolean. And it just says, is this been deleted? We can have a query to show just not deleted users. So my suggestion, instead of marking something as deleted with a boolean, we can mark when it was deleted. This turns out to be pretty useful in the end. And we might wanna keep track of who and when it was deleted. Auditing, you probably wanna know who did things and when they did them. All right, next we'll talk about exponential complexity with booleans. Let's say we have a method to render the document taking each of those three boolean options again. But this time, let's say the states are actually independent of each other and we can't combine them. So how many cases would we have to handle if this method takes three booleans there? It's gonna take eight. So eight cases for three independent boolean variables. If you're lucky, you'll get to write it like this. If you're unlucky, it's gonna look more like that. That's a method with 29 lines of code. Too big to fit on the screen here. That's without doing anything interesting. We're just calling another method for each case. Don't forget the eight test cases you're gonna need. More likely than not, you're probably gonna forget one and you're not gonna have any protection really from forgetting that. So the formula for the number of conditions is two to the power of n if you have n independent boolean variables. That's exponential growth of the bad kind. So our solution here, again, is to represent the state with a single variable. Just passing the state. Now we only have to handle one case for each possible state. Now we're down to three or four cases. Four if you wanna handle the missing case. And a similar number of tests. So let's move on to boolean operations and boolean algebra. This won't be too bad. Let's start out pretty simple. So there's this little square corner sign that's the boolean algebra symbol for not. In Ruby, we use the exclamation point, frequently called bang in Ruby. I think that came from pearl, perhaps. Sometimes you'll see a squiggle or a tilde. And a lot of times in mathematical notation or I guess sort of electronics notation, you'll see an overline, a line over a variable or whatever is being used to represent the boolean value. Note that Ruby does have the tilde operator, but that's for, it's really what you want. It's used for binaries, not for booleans. So not false is true and not true is false. So this is called the truth table. This is one for negation. So if you've got x of zero, then not x is one and vice versa. So we often think of true and false as one and zero or on and off. Comes from electronics. In fact, electronics has their own symbols for this. So that's the not symbol. The important part is actually the circle there on the right side. So that says whatever input comes in on A, the opposite output will go out on Q. So the and, the fancy word for and is conjunction. In boolean algebra, it's like a carrot symbol or an inverted V. That's called the conjunction symbol. If you remember sets, intersection is upside down U. Kind of the same thing. The intersection is things that can take items from one set and the other set. So there's actually a symmetry between sets and booleans. In Ruby, we use ampersand and ampersand. You may see multiplication signs. I'll talk about that in a bit. Ruby also has a single ampersand for, again, that's for binary and. And there's also the and keyword, but you probably don't want to use it because it has a different precedence. If you use RuboCop, it pretty much tells you you never to use it. So stay away from that unless you really know what you're doing. So here's the truth table for and. On that last slide, I showed that multiplication sign is an alternative notation. If you look at that, if you treat them as zeros and ones, you multiply them and you get the answer there for the and. I did not know that. Here for electronics is the logic gate for and. The fancy word for or is disjunction. So boolean algebra, that V-like symbol is called the disjunction symbol. Again, like sets, it looks very much like the union symbol and union is something, is about elements belonging to one set or the other. In Ruby, we use the two vertical bars. Again, we also have the single bar for binary and we have the or keyword, but rarely do you want to use the other one of those. Sometimes you'll see a plus sign. So there's some of the options using Ruby, double vertical bars. Here is the truth table. So again, addition, if you just put a plus sign between those two, the first three makes sense and the last one, one plus one is one. Well, there's no twos in binary. So I think you just have to memorize that one. An or gate in logic symbols or digital logic. A couple other operators you might run into, exclusive or, I rarely have seen that symbol. I haven't really seen the symbols for the other two either. There's actually 16 possible operators of two boolean operands, but most of them aren't very interesting, like the one that always returns true. There's a set of laws that govern transforming boolean expressions. A friend of mine came across something like this once and he submitted a pull request to make it clear. So we've got a predicate method. A predicate method just means a method that returns true or false. It's based on two other predicate methods here. Having that explicit true or an explicit false is kind of a smell to me. With the possible exception, if you've got a guard clause, it's returning true or false early in a predicate method. We're gonna need some tools to refactor that to make it more readable. So here's some of the ways you can transform boolean expressions. We can use these to refactor or simplify our code. Note these common pairs, one for an and one for or. It's a lot of transformations for just two possible values in three operators, right? There's actually more than that. One thing I wanna point out, Ruby has short cutting, which means that different code might run depending on which is on the right and which is on the left, but the value you'll get back will be the same either way. So I wanna call one of those out in particular called De Morgan's law. This basically shows you can switch and and or by adding knots, if you see there in the top one. The second set there is just the first set with the side switched and the knots moved to the other side. So really, we only need knot and either and and or and we can replace one with the other if we need to. We can rewrite the third operation in terms of the other two. For clarity's sake, I would encourage you not to do that in most cases. Electronics actually goes further and they only have a component called an and gate which does an and not. For a not, they just use a single input that goes into both inputs and it just knots it. But for our purposes, we can use De Morgan's law to clarify our code. One other transformation is taking an if then else and converting it to use Boolean operators. I had to look that one up and check it out and make sure I got it right. So basically, if you read the right side, it reads pretty close to the same. So if x and y, you know, x then do y and if that's not true, then check x again and otherwise do z. So now that we've got the proper tools, let's get to work. That explicit true sticks out to me but it's easier to get rid of the if first. And I wrote the rule down there that we're gonna use. So we apply that rule and we got rid of the if statement. Now we've got some duplication there. The Sandy Metz says sometimes you need to take a step back to get to where you're headed. So we can use the identity law now to get rid of that explicit true on the right side. Now we can use distributed law to put those two together or to put the two approvers enabled next to each other. Now we have what's called a tautology. X or not X, that's always gonna be true, right? So we're gonna get rid of the second line there, line three, that's gonna be true. We can get rid of true with the identity law again. Anything and true is that same thing. So now we've got just two terms and we can go one of two ways at this point. We could apply DeMorgan's law but I don't think that's any easier to read than the last one. I would actually go with this to refactor it, extract some methods to make things more clear. So don't be afraid to extract a method even if it's just one line and even if you're adding just a knot if it's going to make things more clear. Even if they're used in just that one place. Note that I did a lot of small steps there, right? And typically I do more than one step at a time but man, you really wanna have tests that you don't mess things up. Oh, what did I just hit? Oh, okay. All right, so the question is which of these would you rather come across? Which is easier to figure out the meaning of? I would say the second one is easier to read, easier to understand, easier to change than the first one. So what's the point of this? The original code that we looked at all worked. So why would we change it? We read code a lot more than we write it so we should optimize for reading and more importantly, we should optimize for understanding. Abstractions are meant to help us understand things. The Sandy Met says, take the time to find the right abstraction. Sometimes that'll take some extra work. Writing good code will take longer in the short run but in the long run it'll pay off. So I hope that I've shown that there's more to the Booleans than meets the eye but the bigger point is we can make our code easier to read and understand. So take a little time to make it easier for the next person who has to read your code. More often than not, that'll be you and even if it's not, it's the right thing to help your teammates out. So thanks for coming. Thanks to Jeremy Fairbank for inspiring this talk. Amos King who provided some of the examples. My local user group in St. Louis. My team at F5 and RailsConf for choosing my talk. And thank you to my employer F5 who sponsored my travel. We do have a few positions open for web developers. If you're interested you can come see me. So a coworker told me this joke and it's the only joke I could find about Booleans. Best thing about a Boolean is even if you're wrong you're only off by a bit. I don't know if that's that funny. So one reason I give talks and conferences is to start conversations. Please don't hesitate to come and talk to me. And you can find the slides there, the source. I used a tool called Remark to create these slides in HTML. You can tweet at me, check out my GitHub. Check out the presentations on GitHub or send me an email. Thanks for your time.