 Wow, it feels amazing to be up here. How is everybody doing right now good? I know yeah, isn't this an awesome conference. Yeah, come on people give me some clapping. I got to get some energy. Yeah, thank you All right, so today I'm going to be talking about something that's a bit weird to talk about at a Ruby conference I mean talking about type safety and that's something that you normally don't think about when you're coding in Ruby because Ruby is dynamically typed and everything sort of happens at runtime We sort of ship it out there and see what happens when we run tests or when we run it in production Before we get started though, I'd like to introduce myself. I'm Ryan Levick. I'm itchy ankles on Twitter. Please don't ask And I wanted to tell you something I absolutely love Ruby I love it to death I work at at Wunderlist we use Ruby all over the place amongst other languages But really my first love in programming was Ruby so much so that I really want to give a shout out to this guy up here Matt's Without him without the contribution of everybody who's who's contributed to Ruby I would still be doing marketing and that would make me sad Basically this was me before Ruby right here in fact And and this was me after so so Ruby has truly truly made a difference And and why is Ruby so great? Why? Because Ruby is readable and it's expressive and it's flexible and these are all things that I don't need to tell you Because you already know that or you wouldn't be here if you didn't believe in this already You know it's not like this what we have in some other languages where we have the full the full language And then there's a little subsection. That's the good parts Ruby is sort of is the opposite of this most of it is the good parts I thought Ruby was perfect when I started out with Ruby. I thought how could there be anything better than this? This has to be the best But in fact No one is perfect not even Ruby For one thing Ruby is pretty slow, right? I mean everybody knows that Ruby is slow We have things like Rubinius to help us with this and J Ruby That's sort of a given But what else does Ruby lack and we're gonna be talking about one of one of these things that Ruby lacks Let's take a look at some examples here We have a simple a simple method that adds one to whatever we pass in here And really what this says is x has to be something that responds to the message plus, right? That's all this really says But what happens if x is nil, right? So we might do something like this and hopefully hopefully all of you are looking at this and say I would never write something like this Because this is some ugly ass code right there really really not good, but it's it's very defensive I'm gonna make sure that we don't end up with those ugly nil exceptions There's other things we can do we can go ahead and say unless x responds to plus raise some argument error and Hopefully that will catch it before we end up with those weird undefined method plus for nil class things way down the line But really what we end up doing instead of these defensive tactics is we just test the shit out of our ruby code We throw test after test after test at it Until we end up with this this code right here that I'm testing here is only three lines of ruby code And I put 244 tests on it. That's pretty crazy, right? I'm kidding. It's actually a full rails application, but But it sort of seems like that sometimes we have to just throw test after test at it until we feel somewhat okay and confident that what we've put into production works, right? We we still end up with errors though we still end up with things like this we have all these services out there Where we catch these exceptions in production and you know on Monday morning because it's Friday We don't want to deal with it and you know on Monday morning. We'll come back and we'll fix this error But until then our customers out there will be experiencing this sort of exception So how how can we fix this? How what is there out there that can prevent this from happening? If only there was an automated way to find these errors if only It's called static typing Right, and you might think Did this guy just mentioned static typing at a ruby conference? Is he insane? Don't we hate static typing like static typing is evil, right? This is the setting where you write static typing in right No, I don't work in a cubicle. This is what I wear to work I'm actually dressed up today normally I show up in a bathrobe, but This is what you where you would work if you if you use static typing and No wonder we feel like this This is probably some incorrect Java code because I don't care about Java But you can see that we have to say end and end and end and all these return statements And who wants to write this crap like shouldn't the thing just figure it out like why do I care and the answer is well Actually, there are languages out there that are not Java That do this way better We have something called type reference that helps us with this kind of thing Does anybody have any idea what language this is right here? Yes, this is Haskell and this is fully statically checked so you cannot pass anything Let's say it determine it will determine that x's and ints really it determines it's some number And it will check that before you run your code So we know that we can't end up with those nasty errors that we've done something wrong because right before we even run the code a Compiler tells us hey, you screwed up man. You you shouldn't do that And we don't have to annotate it with all these ugly end here and there blah blah blah You know the computer does that for us because we're lazy so Okay, hopefully I've at least shown you that we can have this static typing And we don't really have to pay a huge price for it in terms of annotating everything with their types But what does this actually get us what even if this is true? Why should I care? I get along great with my ducks and Ruby right? So let's take a look at an example so hopefully this isn't too much code to go through this is some Ruby And basically what it is is a method called decorate user that takes a user And then delegates off to a user decorator and when we actually call this we first find the user We decorate the user and then we you know we respond or something like that And then when we run it we end up with an error like this and the thing is if we look at where that error is happening It's happening in user decorator line 30 now the problem with that is that that is nowhere near where the problem actually Bubbles into effect if anybody notices when we look back here the real problem most you know rails developers will Spot it immediately. You'll see that fine by of course can return nil But then it gets passed into the the user decorator oops It gets passed into the user decorator goes all the way down probably gets thrown around a bunch of times And then we finally get our error and we have to go in with our debugger open up pry or something Our Ruby debugger something like that and finally you know six hours later and 13 cups of coffee We found our our error now Maybe I'm exaggerating in this particular example, but there have been times I know everyone is out here that they've searched for an error basically essentially a type error And they've spent hours doing it until they find that magical line that screwed everything up But there's a way to avoid this kind of this thing with nil where nil can nip us in the butt So this is our first example of where having sort of a statically typed Type here can help us avoid these errors and that type is the option type So I'm using sort of Scala syntax here mostly just because it's semi ish familiar to rubious But hopefully nothing should be too scary for anybody in this room I'm going to have this type called option of ent and we can have option of anything option of string or option of my type option of user option of whatever and some particular values of option of ent are Either there's some let's say five or some six or some seven or there's none and you might say well Okay, what's the difference between having five or nil here? And the difference is what we'll see in a second when we get our error Now here's the same exact code that we had before except this time. It's this is actually our Ruby code This is the code written in Scala And it's using the option type so user dot find by will actually return an option of user instead of user Or nil and what that means is when we run it We see whoa, whoa, whoa our user decorator here expects to receive a user Or an option of user and are excuse me It expects to see receive a user and you've passed in an option of user You need to basically convince the line of path in your code that this thing actually there is a user there And of course there are ways to do that that dive into the Scala syntax that we don't want to talk to but Here we can see that the problem in our code happens in our code Right where the problem arises instead of somewhere deep inside of our our user decorator class So there we can have we can basically with this type completely 100% eliminate these types of errors Undefined method whatever for no class or or no points or exceptions in Java. They don't need to exist at all There's no reason for them to there are patterns codified in types that basically allow the compiler to prevent us from making these mistakes Because you're never going to want to call name or email on nil. You just will never want to do that But wait wait wait wait Isn't this also a bit more intention revealing as well doesn't this expose our intent a bit more than what we had before Let's take this sort of method Signature here and if you're not familiar with the syntax basically this is fine It takes an ID of type int and returns a user The only problem is we have a tiny bit tiny little asterisk. Can anybody read what that says right there? It says it may return nil. Maybe we don't know it's sort of hidden off in the documentation somewhere And we have no way of enforcing this except for us to be good developers and make sure that we check for nil or You know hope that we always find the user But if we do this if we say find actually returns an option of user well, then we know okay The user could be there or it couldn't we've sort of exposed the truth to the type signature We've sort of told everybody we've documented in our code the fact that this is a possibility instead of Implicitly relying on the fact that oh, yeah, I know this could return now Let's take another example This is like a fake thing for redis fetching some some value for a key in redis and here we have the same exact thing We return a string well except you know the connection to redis like screws up But maybe it'll raise an error, but we don't see that in our documentation We don't see that in our type signature We're just relying on the fact that you know I read it sometime or you know Bob over there told me Mary told me yesterday that this is a possibility Instead we can do something like this We have the same type of method except it returns either an error or a string So we sort of exposed again the fact that this thing can raise it not raise an error But return an error or return the actual string. We've brought it up to the forefront But you might be thinking whoa Okay, this is this sounds okay I guess, but I mean isn't this what tests are for like I write tests so that when yeah Maybe I get an error when I run my test, but it won't go into production. I swear it won't But I have a question does anybody know what the best testing library ever invented was does anybody have any clue? You can pilot close But not quite there's one more that's better and Tyler uses this as it's as background testing library Puts no Puts debugging is effective sure, but it's incredibly frustrating Yep puts this and then you change it to the next line and the next line. Oh, that's really that's really frustrating No, the correct answer is actually Something that this guy used does anybody have any clue something that this woman used There we go Something that this guy used as well. Yeah, that's right. It's math math is the best testing library ever invented Why is that because math is true? If anybody in here can come up with a way of proving that math is not true then I will hand you your Nobel Prize Congratulations What's that? Yes, the undecidability Something is I would love to talk to you about that we won't get into that But it turns out that most of the time undecidability when it comes to types is not relevant These things are usually decidable So what I'd like to talk in terms of math is about something that these two gentlemen came up with It's called the curry Howard correspondence And it's something that this is mr. Curry and mr. Howard here If anybody knows of currying and functional programming or the language Haskell the guy over here on the left Gave his name to both of those things So basically what this means in layman's terms is that for all inputs a valid and correct output is given I could give a more formal definition, but we don't need that. This is Essentially correct. It's always present and cannot be forgotten just because you know We forgot in our documentation that this thing returns an option of user doesn't mean it's not there. It's there It cannot be erased Who here let's be honest be honest with me who here has erased a test before it's been failing and you erased it Yep, everybody else here who's not raising their hand is lying. I know it Or maybe I'm just a terrible programmer. I don't know so Also the compiler is far far far less likely to be buggy than your tests are of course the compiler can be buggy But you have lots of people looking at the compiler and you have you and your buddy working on your code base and that's it What's what's probably going to be more correct something that's tested in millions of code bases or Your tests that you wrote late on a Friday night and what this really lets us do is sort of Test what is important. I'm not suggesting that testing is unimportant No, no type system that we've come up with so far will make sure that you're returning The string Bob instead of the string Mary when that's the user's name. It's just you need to run tests But what you test is your business logic? That's all you test and instead of testing these incidental things this incidental complexity that you've introduced you test the things that truly matter and That's wonderful. That's mathematical and if you're wondering why I chose this picture It's because it's really disturbing and I like that so So let's look at another example. We'll look at a function called head And what head it does and Ruby usually call this first? It's taking the first thing and a list or in an array and Here we delegate to that first method in Ruby. We take a list and we call first on it and we get that item out Of course if the list is empty then we can raise an argument error because we don't want to return nil We just we want to always return the head of the list and an empty list has no head so okay, if it's empty, let's raise an argument error or You know we just learned about this awesome option type So let's get freakin fancy and we'll return my Ruby option type This is a terrible idea because Ruby is not built for this So this is just kind of confusing and not idiomatic and people will probably get be confused I've tried to do this before and I just erased my entire code base because it's not it's not pretty in Ruby So let's look at an example of a language that does support something like this That really brings these ideas to the forefront has anybody here heard of Idris before Do we have any Haskell programmers in the room any at all? Oh Very very few. All right So this is the new sauce for Haskell programmers. They've sort of moved on so if you thought Haskell was Was the crazy stuff. This is what Haskell programmers think is the crazy stuff We're going to be looking at this this is the head function and Idris And we're going to we're going to dissect it piece by piece because obviously if you've never done functional programming before or you've never Looked at Idris. This could be a slightly confusing, right? So we'll just go one section at a time We have our function called head here. That's all this is saying this colon right here Just means everything after this until the new line is a type signature. That's all this is saying and Here we have our arguments for our function now Let me break this up into the three parts that it is one VEC that means it's a vector So it's a basically an array of variable length The second part of it the s of n is basically saying this vector cannot be empty If you don't understand why that says that it doesn't matter because that's not important for this talk But trust me this says this vector cannot be empty I am encoding it in the type system and then the a says that this vector is filled with things of type a whatever that type is int string bool my type whatever user who cares and Then the next part says we will return something of that type a So if the vector was full of strings, we will return a string if the vectors full of ints we will return an int The next line we have the implementation of that so again We say this is our implementation for head And then we sort of do something that rubies are somewhat familiar It's pattern matching and destructuring based on that pattern match So we know that we have one argument here And what this says is I'm going to break that list that vector rather and to a head and then rest or tail So I will have the head of the list cons on to or prepended to the tail of that vector It's sort of like oops It's sort of like this and Ruby where we we break up the array and we get x and y out of it by by Destructuring that argument and then we return that head. So we have effectively gotten the head of the list So why is this so cool? Because this when you run head on an empty list you get this error Now this may seem like what's the big deal? We got errors in Ruby as well. This happens at compile time. So this code will not even run It's not even actually being run with real values in it. It's saying this is not a valid program This breaks my type Just like you can't call In Java you cannot pass in a Boolean where you said you pass in an int This will not allow you to pass an empty list when you when you've said that you will pass in a list with things in it So that means that we can basically with this eliminate all runtime errors There's no need to have a runtime error because all incorrect programs all programs that will throw an error We'll go ahead and be caught at compile time Has anybody ever seen this guy before Michael Bernstein? He's an awesome super awesome Rubyist that everybody should watch his talks on YouTube and He's a personal hero of mine even though we've never met And he is really into this kind of stuff He's a rubious, but he likes type systems and it actually has gotten quite deep into them he works for code climate and At code hit one day he decided well, what if we bring these ideas to ruby? What would happen? So he built something where you basically annotate your your methods and ruby with type signatures So this says do not even like run be able to run my ruby code unless I can prove that this is the type of this function and There's also other dynamic languages like Erlang that have sort of a similar idea and Erlang you have something called the dialyzer Which basically statically checks your dynamic Erlang code You can annotate your types and say this is what this function accepts and if it doesn't then it will just won't run It will never run But I gotta ask is this what we want like doesn't this defeat the purpose of ruby Ruby is dynamic and that's that's great. That's why we love ruby. Huzzah ruby is awesome But There's one thing Ruby is a tool. That's all it is You are not married to ruby Ruby is not the end-all and be-all it is not the the end solution to every problem that you will have No, no that when you when you choose ruby You are giving up the power that we have just seen here You will never have this in ruby and if that's okay for your domain and your problem then great But maybe it's not maybe you need that reliability There's a really great book called Zen in the art of the motorcycle maintenance that me and my my colleague Chad Fowler I've talked about quite a bit. There's an idea in that book called value rigidity Where you basically Become so in love with a concept that you can't even critique it and that's my fear for the ruby community Ruby is amazing and we all know that but that doesn't mean it's not flawed That doesn't mean that we can't look at ruby and critique it for what it is So don't be afraid to ask yourself when you're starting a new project Is ruby the best tool for this job because the answer may be no and that's okay We should know what we're giving up when we make these choices We should know that we've made a choice in the first place and the choice isn't given to us by the fact that we're in love with ruby Thank you What kind of practical problems have you come up with a wonder list where you thought okay? We can't use ruby here for for type problems, I mean at my day jobs It really normally comes down to performance above anything that's due to our architecture We write very small code bases where if we were to write it we wouldn't necessarily need to have something that's completely Type safe because the code is so extremely small that we can essentially get by without that But there are definitely places outside of that like When you're writing a compiler itself or you're writing a web server where these ideas really do come into to Play because you want a system that you can change and know that you don't have to have Written good tests to make sure that it works. You can rely on something. That's mathematically sound in order to ensure that it's correct Other than that there are just a lot of languages that are have these things and they're a lot of fun to play with like Haskell For instance, which we do use Any more questions I'd be happy to talk about this at any time. I'm obsessed with this stuff. So I will come find you if you don't ask me a question Let's give it a really warm hand