 Because the user sent in bad data, the application can error before it even gets to the database, possibly tell the user something a little bit more useful in terms of what they did wrong if they're using an API. And then there's a lot of simple patterns that just come with the RM that make it easy to build out an application quickly. That's why things like Django and Rails and stuff are so popular because you can type a few things in and you get like an application really fast and that's definitely really cool. But there are other ways to get at this. So composition is, I wrote functional composition here, but I mean even object composition is good generally better than inheritance, not always, but generally. Being able to compose things makes things more flexible and also lets you hide things. So if instead of a subclass, you contain a class, you can hide that class from the external world and you can trust that I've got my model class, but only my model class can talk to the database, nobody else can talk to the database, and I don't have to worry about queries leaking in somewhere I wasn't expecting or things like that. So composition is a great tool for abstracting away complexity that you don't need to know about in the function you're writing or in the module you're writing. There's a lot of built-in data structures in Python that provide a lot of these, have a lot of power to them. You can also, like I said, you can just write a class, you can build your own class hierarchy, et cetera. There's one more thing which is going to be the main focus of my talk that you can use, and that is MyPy. So it talks about MyPy, it talks about RMS, what do they have to do with each other? They actually have nothing to do with each other. MyPy has nothing to do with databases. MyPy is a static type checker. Has anybody used MyPy? Mostly on that side of the room, okay, a few people like that. So it's a static type checker. You can use it to tell, it will tell you if you're doing something wrong in your Python code without actually running your Python code. And it uses type annotations if you're using a version of Python that supports type annotations in the syntax, alternatively you can run it against old code, you can run it against Python 2 code and you do your type annotations in comments and it can parse the comments out and figure out the types. So let's show off a little bit of what it's going to look like. So I'm going to open up a file here, sure, oops, nope, hitting the wrong character. So I'm going to write a function and I'm going to annotate its type. So this is just from the MyPy example but just so we see what it is, I've got greeting, it's going to take some name, we're going to give it a type which is a string, the colon means this is its type and then the function has a return type and that's what the arrow means, this is what it's going to return, it's also going to return a string. And there we go. So right now, there's no errors because the function isn't actually doing anything. Even though if you call this function it would return none, MyPy is saying that's okay. You haven't actually written anything yet. If I return an integer it will tell me incompatible return type, I expected a string but I got an integer, you're doing something wrong. I can say okay, well how about just saying hello and it says okay, cool. One thing to note here is that I'm taking the name, I'm not doing anything with the name, that's probably not what I intended. MyPy is good at checking to make sure that you're handling conditions, that you're not accessing things that aren't there, stuff like that. What it can't do is tell you if you're doing the right thing because it doesn't know what the right thing is. So there's a lot of discussion about types and tests and which is better. You really kind of need both if you want to be really confident that your code is working correctly. It's not a replacement for writing unit tests but I do find that it's helpful for guiding you saying hey, you forgot about this case, maybe you need to handle this case. Probably want to test to say that the right thing happens in that case but it will at least tell you that you handle the case, you don't handle the case. So here, similarly if I say hello plus five it's going to say I can't do that because you can't add strings and numbers but if I say hello plus name it's totally fine. So down here I just have my editor telling me what's wrong with the current file I'm working on. So let's start using MyPy instead of an OR app. So the very first thing we want to do is define sort of our model. I'm going to use something called a typed dictionary. Comes from MyPy type extensions. I think at some point in the future this is going to be merged into the standard typing module but for now it's in an extension while they're sort of solidifying the API. And I want to, I'm going to define, let's say we're building an application for dealing with books because books are, you know, really cool and modern and etc. So I'm going to use a typed dictionary. A typed dictionary is just, it says look, at runtime represent this object as just a dictionary but I want to say what attributes it has to have and what types those attributes are. And so books have authors, sure, and they've got titles. So there we go. I should probably type out the code correctly every time. Okay. So that's useful maybe. Haven't done anything with it yet. Let's write a function that can take a dictionary. Maybe it's coming from the user json request somewhere else and return one of these things. So we get a dictionary coming in, call it request. It's a dictionary but we want to get a book back out. So we can return a book, not a book, and it has an author, okay. So it says that's, okay. So we've eliminated all of our typos and it says everything's cool. Now one thing you'll notice here is that this function won't always run to completion. If I pass a dictionary in that doesn't have these arguments, it will raise an exception and won't return anything. It won't return a book, it won't return anything. Maybe that's fine. Maybe we want to do something a different way. We can talk about other ways to do it at the end of the talk if I get that far. But just sort of be aware that static type checking works great on functions that return values. It doesn't work great on things that raise exceptions. It can't tell you that you mutated state necessarily. It's really about pure functions or what it handles best. So we've got this function. We can give it a dictionary, a generic dictionary. We have no idea what's in it and it will either return a book or it will raise an exception. Okay. So let's say we want to be able to save this book into our database. So I have written a fake database here that I'm going to import and it just has, it has an insert function. So I'm going to, I want a function that saves a book, takes a book to save. The book has to be a book, not a book. And it doesn't return anything. And then I'm going to insert the book into my database. I'm going to pretend like it's sort of a MongoE except I called it insert instead of, instead of insert or whatever, whatever. It's got a, I want to save the title and I want to save the author in the database. Everything's good. Cool. Authors are good and all, but the publisher is where the money is. So we really need to keep track of the publisher because they are the people with the money. So we are going to, first let's pretend like, you know what, we need to remember to save the publisher. Oh, that's interesting. It's actually telling me that I can't do that because books don't have publishers. So I can come up here and I can say, okay, books have publishers now. Oh, but now it's saying, I can't create a book without a publisher because I said books have to have publishers. So now, so you can see what it's doing here is it's kind of guiding my refactoring. It's letting, it's sort of telling me, you forgot about this, you forgot about that. It's keeping track of what you can and can't do with the values that are passing through your, your code and it's telling you if you're doing something that will potentially fail at runtime. So that's kind of cool. So let's add something else. So some books come in series. Not all books come in series, but some books are a book in a series. So let's try to model that. So we're going to start out by just defining a new class, book in series, and it's kind of book. You can inherit from it. It's not really inheritance because remember at runtime these are just dictionaries, but it's sort of a hint to the type checker. By inheriting from book it gets all of the attributes of book, and then it can have new attributes. So I can say I also have a series. It's just the name of the series, the ID, whatever. I'm just going to store it as a string. Okay, that is cool, but it's not really doing anything useful right now. But we can use it to add a new feature. Let's say our website, we want to be able to find things, books related to books. Recommendations and engines were a really big deal 15 years ago, still are probably. People don't brag about them as much because they all just bought them. So we're going to write a function called find related, and it's going to find one related book, or it's going to tell me that there aren't any related books. So it's going to take a book. This type is book, and we want to return, we want it to return a book. Makes sense, right? But I don't really know how to implement it yet, so I'm just going to pretend, I don't know. I'm going to figure something out. Okay, so how could we go about finding a related book? Well, if the book is in a series, then we can just find another book in the same series, right? They're presumably related. So we're going to write a function, find in series. That's going to take a book, but that book has to have a series, so book in series. And it's going to return a book in series. Okay. So we want to somehow do this. I've got this database class, it has the select function, or not class, module has a find, I actually call it find. And see, I want to say, again, sort of pretending like we're writing against Mongo. Mongo is interesting in this case, because it doesn't enforce anything, so you really need to enforce things at the application level if you want anything enforced at all. So I'm going to find a book here. Okay. Got to return the thing I find. Okay. This is interesting. So the find implementation I wrote says, I'm not always going to find the thing you're asking for. Maybe that thing doesn't exist in the database, so it uses something called optional. So I'm going to go import optional here, because I'm going to need it, optional. Okay. And this is returning an optional book in series. Still not right, because I'm trying to return an optional, let me, if you look at the very bottom here, I'll tell you the whole problem. It got an optional dictionary of anything to anything, but I told it I was going to return an optional book in series. So that's because I need something similar to a parsed book, but for a book that has a series attached to it. So just copy paste. That's a good programming practice. So I'm going to do this book in series, and I want it to return a book in series. Okay. I'm probably done right. No, I'm not. Okay. So we need to return. It says I need to, I said I would give it a book in series, but I gave it a book, so book with a series. Okay. And then I'm going to also break this up, because it's starting to get a little long. So I should be good, right? No. Okay. So a book in series needs a series. I told it it needed a series. It's telling me that. It's saying key series missing from type-dick, dicked-booked-in series. So let's do that. Okay. So now parse-book-in-series is written correctly, but this still isn't written correctly. So what I'm going to do is I'm going to say, well, I'm going to parse-book-in-series, and that will turn my JSON object thing, dictionary, into a book in a series. It seems like it should be a good idea. Still won't let me do it, because I typed it wrong, because I typed a book twice. Book book. Okay. But it really wasn't supposed to let me do it anyway, because parse-book-in-series expects to get a dictionary, but the database may not return a dictionary. It may just return none. Optional means you're either going to return this, or you're going to return none. This is sort of a subclass of another type called union, which is one or the other. The other is always none. So here, again, it's telling me, hey, you forgot that sometimes the thing you're looking for isn't in the database, and you don't get anything back. And if you run this, you'll get like a none type has no blah, blah, blah. Very annoying error. So let's save this off. Save off what we get. And then we can say if row is not none, return parse-book-in-series row, otherwise we'll return none. Okay. Cool. So we can actually go to the database and say, hey, find a book in the same series. If you don't find it, return nothing. You do find it, turn it into this book-in-series thing, and return it back to me. Okay. That sounds cool, but we still haven't implemented this find-related thing. So let's figure out how to implement that. So what we want to do is be able to say, hey, look, when you ask me to find a related book, if the book belongs to a series, I know how to do that. I can just find another book in the same series and tell you to buy that book. If it's not in a series, I haven't figured it out yet. We've got to do some machine learning thing or something, and I don't know. Whatever. We'll just say nothing. So I am going to here say, let's try this. Let's just say find-in-series, okay, there's a couple of things wrong with that. So we're trying to return the wrong thing, but also find-in-series wants a book that has a series attached to it. We're not sure if the series is attached to it, because we haven't checked. So let's try this. If series in book, let's do that, none, because we didn't find anything. Okay. So the first thing is, sometimes we return none, and we said we'll always return a book, but we actually know that we're not always going to return a book. Sometimes there is nothing related to the book. It's just like a magical unicorn book. It's genre-defining, and nobody has caught on to the hot new genre. Okay. So find-related has to be optional. We can't always find something related to it. There's still a problem. The problem is that find-in-series wants a book in series, a book with a series attached to it, but we only have a book. So this is actually one of the places that MyPie kind of has trouble. We know, by looking at this code, that at this line here, the book has a series attribute, but MyPie doesn't. It's gotten confused. Fortunately, there's a way around that, which is cast. So if you've ever written any C++ code or Java code, you know what that does, and we have to import it. Okay. So we've told MyPie, look, trust me, I checked. I'm very certain that book-in-series is the right type. One thing we didn't have to tell it, though, is that a book-in-series is a book because we inherited the types from each other, even though here I'm saying I want to return a book, and here I'm saying I'm going to return a book with a series attached to it. MyPie knows that a book with a series attached to it is also a book. So let's us do it. So that is that. And then one last thing that we need to do, we need to be able to save the book. So I want to save the book. I want to save the book-in-series. And I should probably save the series, huh? Right? No, because I forgot to update the type. So there we go. So the type-checker is kind of assisting me as I'm refactoring my code, and it's saying, hey, you forgot about this. You need to check for this attribute. This attribute doesn't exist. But the other thing it does is it says you can trust that this attribute exists. If this was some whole very complicated sub-module that dealt with books that belonged to a series and it needed to find an image that's the series image instead of the book's cover image or give you a listing of all the books in order, you can trust, no matter how many other functions you call, that the book has that attribute. And if you make a mistake, as I did many times up here, the checker will check it for you. So that is it. And I don't know if there's time for any questions or not. That seems sort of backwards from the normal workflow, I don't know. I'm not going to add a field, I'm typically going to go to the class. Is there a way if I add a field to the class? It's not going to complain, it's not going to have to go find all the things manually. Yeah, so if you go add a field manually, any code that doesn't access the field, MyPy doesn't know you're supposed to be accessing the field. This is a little hand-wavy here. I think that what you would probably want to do is build out that interface layer between your code and your database. You want to unit test that, right? And so you're kind of doing this in conjunction with writing tests. So at some point you'd have a test that says, hey, look, if the user gives me a book that has a series attached to it, I better save the series attached to it in the database. You're going to have to go update the database saving code to write the series out. At that point, that sort of drives you to go start adding the type annotation and then MyPy helps you find all the places that you might have broken and go in and sort of fix them up. So it's not, it can't do everything for you, but it can help guide you and show you places where maybe you forgot that you did something over here or whatever. So that's what it's really good at is discovering those things. Okay? I think that's time.