 Hello. Today I'm going to talk to you about keyword arguments in Ruby. So we'll see why they're useful and how they were introduced gradually in Ruby over the versions and how it all starts with the options hash. So you may already have a good idea of what keyword arguments are, but let's start with the definition. So Wikipedia says they refer to computer language support for function calls that clearly state the name of each parameter within the function call itself. So translated in Ruby, when we call a method, we name each parameter as we pass it an argument. So let's first look at the advantages doing this brings over regular parameters. If we define method in Ruby, we give it an ordered list of parameters. And it means that later when we're going to call the method, the position of the arguments will matter and which is also why we call them positional arguments. So when you look at the method call itself, there's no way you can guess which arguments corresponds to which parameter. So if the call sites are in a different file than the method definition, which happens a lot, you can be confused. So if we look now at the definition of a method with the keyword arguments, we can see it's very similar. But when we read the method call now, it's much more clear what we're going to get. Also now the order of the arguments doesn't matter. So if you're a bit OCD like me, you can order them alphabetically. And we don't need to know the order of the parameters anymore. So of course we need to know the names now. And we need to type them. And they take up space on our screen. But it's more verbose this way. But I would argue it's also more maintainable because it allows you to change more easily. And the only thing that we know, the only thing that doesn't change is that there are going to be changes later on. So it's like that. So this way it's easier to add new parameters or to replace the old ones if we want. For example, we'll see later if we want to support percentage taxes and stuff, it's going to be much more easier than using the position logs. So this can be called cognizance. So it's a fancy name for coupling. Basically it's just in a different, it's for objected oriented design. And what it means is that if two things are cognizant, if you change one, it will require you to change the other. Or at least you're going to have to check the call sites. And in our case, if we change the method definition with position logs, we're probably going to have to change the method calls in other files. So I won't spend more time on cognizance, but if you want to learn more about, oh yeah. So this is just the order. It's an approximate order of the weakness or the strength of cognizance. And name is the basic one. We can't get rid of it. It's the only way. The weakest link we can have between two parts of our programs. And if you want to know more, there's a great talk from the late Jim way rich. So I would encourage you to go see that. So at this point, we might think, oh, let's go ahead and use keyword arguments everywhere. But obviously there's always a tradeoff. So if you have a method that only takes one parameter or even two, but if they're obvious, it's not going to help. And it's going to make your code more verbose and more Java like or whatever. So for example, complex numbers. In all the definitions I've ever read, it's always the real part and then the imaginary part. Same thing for rational. It's always the quotient over the denominator. So we don't really need to name them. Same thing for all the innumerable methods that take a number of elements. It would be silly to name the parameter N or count. It doesn't make sense. Yeah, by the way, you should rather use those literals for imaginary and rational. But so the first thing that you need to know about name parameters in any language is that you can emulate them using any data structure, such as an associative array, like a struct in C, an object in JavaScript, or hash in our case. And it makes sense because what an associative array gives us is that it allows us to associate a name with a value. So that's kind of similar to what we want to achieve with keyword arguments. And you can oppose that to the array. And a simple array is closer to a regular parameter list. So let's go see how we did that for a long time in Ruby. We've used that especially for optional parameters. And we call that the options hash, usually. So you can see that by passing in a hash, we can fake naming the parameter. And we can also pass it straight. And the great thing that we all know and love in Ruby is that we don't need the implicit hash. We can omit the curly braces. And it looks pretty great in this case. So actually, when I was researching for this talk, I found this in the first edition of programming Ruby. Like Ruby 1.6 does not have keyword arguments, et cetera. And what made me giggle was in parenthesis, the schedule for Ruby 1.8. So, yeah. Okay. So if we come back to the example, but now we want to add the percentage taxes feature. So what we can do is just add the default values up there. But obviously, this won't work. So the issue is that the full hash is just a single parameter. So it will override the other one. And we won't get the default value for the tax rate. So over the years, a number of techniques emerged. And usually, with just a line of code, we can get around this. But it's still boilerplate. And also, libraries gave us a way to express this in better ways. So for example, from active support, you can get reverse hash, reverse merge. And makes it a little nicer on the eyes. But in any case, it's something we'd rather have not to deal with. So let's see. Oh, yeah. So this pattern, the options hash is very popular. So Rubyist, we use it a lot. So if we look, for example, at Rails in the latest version, it's used almost 500 times in the whole code base. In Shopify, it's like even worse. Or even more. So let's come back to keyword arguments and try to trick Ruby and use the options hash as keyword arguments. So the simplest thing we could do is just using the values. So all the hash indexing, you can see options index, subtotal. It's not great. But that's almost the code we would like to read. But what could go wrong? Let's say we forget an argument. We get a no method error. And in this case, it's very easy to see the issue because we've got the code. In some cases, it can go deeper in the stack trace and it can get lost. So what we've been learning and what I've degreamed, for example, has been teaching us is we should use fetch, for example, to make it clear, right in this method that these keys are required. But even then, what could go wrong? For example, we all write code sometime in the morning before having coffee. And you don't get a weird result there. And that's because I got a typo there. So once again, you can go back to active support. And you have this method called assert valid keys that pre-helps for this case. Another issue we can see is when we try to combine this using the splat operator and the options hash, we can't do it straight away. So we need another line of boilerplate just to get that out. Yes. But even then, you can see, when I was looking like extract options, I thought it would be just this line of code. We could just use that. But in fact, if you look right now in Rails, they actually do extractable options. And they define it in hash to only work with instances of hash. And then they redefine it in other subclasses to say, okay, this class is okay to also use extractable options. Because they had issues with Mashi or Mongo or like, it always happens. So we really wanted this to come into Ruby itself so that we would always, we would all agree on what the way it should work. So if we come back to Ruby 1.8, this is the same code except I'm using the hash bracket syntax. So 1.9 was really nice. We were really happy. We have this new closer to JSON syntax, and it's much nicer on the eyes. But that didn't change anything about the method definition itself, only the method call. So this syntax works only when the keys are symbols, of course, but that's most of the time that's what we want. Except sometimes it's not so welcome back to that in a moment. So Ruby 2.0 introduced optional keyword arguments. And that was a great day for all of us because we could remove already a lot of the boilerplate because now all the optional parameters that we know they're defined and we know we're not entering a typo or something. So it removed a couple of lines. Ruby 2.0 also introduced the double splat operator, which you add it at the end of a parameter list and it collects all the remaining keywords into a hash. So that gets you back to the state we were before. But we still needed to use fetch if we wanted to avoid silly mistakes. And finally Ruby 2.0 introduced required keyword arguments and we were all happy. I know it was a great Christmas present for me at least. So at this point I felt pretty great and we had keyword arguments in Ruby. But the first time I tried it I encountered a behavior and I was like maybe this is weird or is this a bug? So let me show you. If we have a method that takes nothing and does nothing, it's like it has no parameters. And you call it with nothing. Well, it works, right? But what if we pass no keyword arguments at all? What do you think is going to happen? Any ideas? So the answer is it depends. Of course. So up to 2.0 it failed consistently. But in 2.1 now they started making it work. Actually it was a crash in the parser that happened in the development version. It was never shipped. And the fix actually caused that side effect that now they're just ignored if it's an empty literal hash. But we can still get to the wrong or the weird behavior but just passing value instead. So you could also have an empty variable that holds the empty hash and pass it. That would be the same. So that's what we never do this. The empty, it never happens. So that's okay. So what's going on there? So that's the point where I'm going to argue that really keyword parameters don't really exist in Ruby. So they're not regular parameters that we can access and we can set by using their name. It's like a different thing. It's the same thing as the option's hash. So even though keyword arguments got really much better at doing what we want them to do over the years, still it doesn't feel like they're really here. So let's just compare really quickly to another language that has a more comprehensive implementation of R. I don't know if it's comprehensive. A different implementation of keyword arguments. So as you can see in Python, any parameter can be named. And it doesn't have to have a special definition to be able to be named. And the parameters have a name but also a position and you're allowed to pass any number of parameters by position until you want to pass them by name. So even on the first line, even the optional parameters, you can still pass them as we do in Ruby. So remember the definition from Wikipedia at the beginning. So it mentions function calls. Obviously in Ruby, we would rather say we're sending a message. But that's the same. Or we are calling a method. So when we send a message in Ruby, we pass arguments and optionally a block. And the arguments are nothing more than an array. And there are no keyword arguments in site. So it turns out that the arguments are so similar to an array that we can use the same syntax we use in a method goal in the are literal syntax. So the simplest example with the splat, we can use that in the are literal syntax. But I don't know if you know that. We can also use the hash collecting feature in a narrow literal this way. So that's why I say that when we send a message, we simply send an array of arguments. And we could actually say the method called syntax simply reuses the are literal syntax. But they don't take part in sending in the message. So let's come back to the use case of having any number of arguments and options hash. Now with the double splat, it's much more easier. So that's just a quick thing. So Ruby 2.1, double splat implementation was a bit weird in the sense that wherever you put the double splat, it doesn't matter in the end. So it looks like a double splat in Ruby 2.1 was handled as defaults and any literal value you added to the hash were overriding these defaults. So if we look, I did a huge hash with two double splats. And you can see that actually the values from the outside hash takes precedence over the values inside the double splats. And between different calls of double splat, different usage of double splat, it's the first one that wins. So that was a bit weird. And the way you could get the same, ah, that's too bad. The way you could get the same result would be to merge or to use reverse merge from the rightmost double splatted hash to the innermost one. But you can see the keys are not the same. So actually the implementation would be more like this one. So maybe I can slide. That's pretty bad. So it's just using the block parameter to merge. You can choose what happens in case of conflicts. So in this case, we always want the existing keys to stay. And we want the new keys to just be discarded. And this preserves the order of the keys. And this was the way it was handled in 2.1. Actually Ruby 2.2 changed that. And now it's much more useful. So what's going on now is just simply a number of merges that happen. And they depend on the order of the keys. So now if you really want to, you can override a literal key with an option hash by splatting it after. And if you have multiple hash being double splatted, it's the rightmost keys that win. And the equivalent in Ruby code is really much more obvious. It's only merging stuff from the left to the right. Okay. Let's go now to the implicit conversion. So one of the best things about Ruby is that it's the duck typing that happens in multiple places. Like you all remember the symbol to proc hack that was then used into Ruby. So what happens if you pass any object and you double splat it is Ruby complains because you don't provide an implicit conversion. So implicit conversion methods are the longer named ones. So in this case you can just define two hash and it will take the result of this parameter and merge it into the enclosing hash. The problem is that with the implicit conversion to hash is something that is pretty generic. It's already used, for example, in the merge method. So if we might legitimately need string keys, but that won't work at all with a double splat, of course, because they want only symbol keys. So it might be nice if Ruby was using explicit conversion, especially since we're actually explicit about the conversion there. But and it would call two H instead of two hash. But that's not really possible because when you just want to call a method and pass an object, that's the implicit conversion. So I don't know what's the best solution is, but I leave that as an exercise to Matt. Let's talk now about performance. So it's not the most important thing, but still it does matter. In Ruby 2.1, calling a method with keyword arguments was pretty slow. So this does 10 million method calls. And if you do the same with a simple hash and you pass it, it's about one second. So it's really, really slower than using regular arguments, even considering the creation of a hash. In Ruby 2.2, that was basically solved. So we get in the same ballpark as regular arguments. And the way it was solved, it has to deal with the VM. So let's take a small detour into the VM for that. So if you want to see the bytecode generated by the Ruby VM, you can just run this line by providing your code in the string, and it will output the instruction sequence that are going to be run by the VM. So in this case, you can see the first step is actually defining the method, because as you know, defining a method in Ruby is only code that we run. And the second part is actually the compiled method. So we can see this method just does nothing. And to talk about the previous talk, the traces calls are what allows Ruby to tell the system that these haven't happened. So I think the 8 must mean method entered and the 16 is method exit. Yeah, so let's look at the way the Ruby 2 and VM was handling a method call with keyword arguments. So it was actually building a hash from an array. So it would just put all the key value in an array and then call hash from array. Apparently that would have been the faster way to do that. And then it would send the method call, which you can see maybe there, total. So that was okay. But the bad part was the method definition itself, the method itself. So that's the code that is generated for an empty method. There's not even a code there. But you can see that all the method declaration was just hidden code that you didn't have to write yourself, but it was still there. So it first checks, does the hash has a key subtotal? If it does, delete does the hash and it does that for each of them. So this was pretty wasteful. So in Ruby 2.2, they changed that and now it fits in a slide, except it slides. It scrolls. So what happened now is that actually you can see keyword 3. So now that instruction knows the keyword argument it gets, and they're just put on the stack as regular parameters. And it's inside the VM itself, inside the handling of the method, the message send that Ruby can pass the parameters the way it should. And we can also see that the method itself is now empty, as we would expect. But there's just a declaration of all the keywords that it can handle. So that's what made it fast. And that was nice. Let's continue. Ruby 2.2 also brought us newer hash syntax. It used to be impossible to use keys that were not identifiers with the new hash syntax. And they added that. So the logical conclusion is that we can now copy and paste JSON in IRB and it works. Almost. You just have to define null as nail. And it will work. So I don't think you should do that in your code that you commit. But for IRB, it's really useful feature. An issue that we get with keyword args, as you can see is that sometimes they can get repetitive. In this case, I think I think we could do better. Since create is like a factory method, it could just take any number of keyword arguments and just pass them on to the new method call. And that would remove all this repetition. But that's something that you will see if you start using keyword arguments a lot. Sometimes it's a bit repetitive. And sometimes you wish we had something similar to ES6 where you can pass arguments straight and it knows that you want the same name as the key and value. But to come back to my original issue with the keyword arguments in Ruby, this is an example that shows the issue. You can see that if you pass no keyword arguments to dispatch, it will fail because nothing doesn't take any parameter. So the way right now that we can fix that is by adding some boilerplate code. So by checking if the hash is empty before pushing it onto the args array, we can make sure we don't send an empty hash to a method that doesn't take any keyword arguments. That's about it. I just want to conclude by noting that most of our troubles with keyword arguments were solved by Matt's and the Ruby core team over the years. It looks like this is something that could be fixed also in the semantics of a message send. We could say if the keyword arguments hash is empty, don't push it depending on the arity. It could be something like this could be pushed down in the VM and we wouldn't have to care about that. And I actually expected to be solved in one way or another in the future. So that's it. I'm at the end by A. You can find me at BE or BE by I. I work at Shopify and if you have any questions, I would be happy to answer them.