00:00:09.170
So, we're going to start off today with our keynote. Now, I am not the keynote today.
00:00:14.809
Every year we try to get Matz to speak.
00:00:22.279
Welcome to Keep Ruby Weird! Every year, like
00:00:29.359
we weren't sure which year we were going to end this, so we actually just recorded Matz saying 'Welcome to Keep Ruby Weird' and then a whole bunch of years after that, so that we could play it each year.
00:00:38.250
Now, this year, we wanted to try and get Matz to record something special for us so that we could play it for the very last year; but we weren't able to. Unfortunately, we're just going to have to do it live.
00:00:51.480
Alright, we're going to play the little laptop dance here because since this is a surprise, we did not test his equipment. So, bear with us.
00:00:57.210
Morning everyone! I was not supposed to be here maybe, but yeah, this is the conference. Keep Ruby Weird. It should work.
00:01:09.000
Okay, it's Ruby weird. What's your opinion? [Audience response] Weird? Yes? Oh no, yes, okay.
00:01:20.240
Ruby is neutral. Ruby is only abusable so that Ruby, which is weird, because we're weird.
00:01:28.400
Witness in our mind, so we are weird. We program Ruby weirdly. So, the Ruby is weird. But actually, I admit some parts of it are weird.
00:01:39.170
So today I'm going to talk about this weird thing. The weird thing is the keyword arguments.
00:01:45.830
In Ruby 2, which was released in 2013, which is the 20th anniversary of the Ruby language, we introduced keyword arguments.
00:02:01.210
In cubed Eggman's, we can look like this system with something called comments. But when we failed, we returned an exception.
00:02:11.989
Instead of... you know... so that we can provide the keyword arguments like options.
00:02:18.100
There are many languages that have keyword arguments as well, like how Python has keyword arguments, Smalltalk, C, and Swift, for example.
00:02:25.370
Each language is different; for example, in Python, they don't provide the special kinds of keyword arguments.
00:02:29.799
Every argument can be a keyword argument in Python, like this: def foo(a, b, c) defines the arguments named a, b, and c.
00:02:37.329
So that you can call those functions by providing the name of the argument so you don't have to specify any keywords in the method definition.
00:02:49.419
You can just call them by the name of the arguments, so we can call it like this: foo(c=2, b=3).
00:02:56.380
That makes a=1, b=3, and c=2 because c wasn't assigned and 3 is assigned to b, right?
00:03:05.639
You understand the ball. So this is quite uniquely Python programming I have written.
00:03:12.510
In Smalltalk, the keywords are part of the message or method name.
00:03:17.379
Like this: you can put the number 42 at the position 0, so that that works like a new array at index 0 equals 42.
00:03:24.549
In this program, the 'put' is a part of the method name. We often call it a put–colon notation.
00:03:32.260
This is the method name. In Smalltalk, these kinds of keywords are very crucial; they are mandatory in the language specification.
00:03:40.040
This characteristic of method names is inherited by Objective-C and Swift. But in Ruby, keyword arguments merge at the bottom of the arguments.
00:03:48.770
So you can, like, call a method to print the arguments when you call this method with the keyword argument.
00:03:56.200
You can just print the hash. Let me explain the background in Ruby version 1.9.
00:04:03.639
We introduced two things: one is a simple hash, and the second is the expanded hash in arguments.
00:04:10.850
Simple hash means the hash looks like this: :foo => 1, :bar => 2, instead of the hash with the arrow symbol, which is a simplified version of the symbols as keys.
00:04:22.350
Expanded hash arguments means like this: instead of writing the first column of the hash, we can write the first column inside the braces.
00:04:30.670
So we introduced these things to mimic the keyword argument.
00:04:36.900
In Ruby 1.9, we could do some kind of keyword argument as an optional argument, then document keywords like using Mirage, or something like that.
00:04:45.200
This is better. You know, this kind of manual parsing keyword argument is contained.
00:04:52.070
So, we introduced keyword arguments in Ruby 2 to avoid manual parsing.
00:05:00.230
For example, to implement the previous one monotonic argument like this, instead of writing this one in Ruby 2, we can do it like this.
00:05:09.120
Much better, but still weird because what should happen?
00:05:17.600
We define the method 'm' with a mandatory argument a and a keyword argument with a default value of 10.
00:05:27.910
Then we print the argument a and the key value.
00:05:35.700
So when we did 'm key:5', what shall we get? Yes, because this is the hash.
00:05:45.100
So this hash is assigned to a, and because we have the optional key, the default value is given, so we get the hash and 10.
00:05:56.670
Yes, it's easy, right?
00:06:03.679
So then what would happen when we called this with 'h'?
00:06:12.780
It happens like this, because the keyword argument is the last, in the argument list.
00:06:19.490
So they will be considered as the keyword hash. This means the keyword argument is used, then key will be 5.
00:06:29.390
What should happen? Yes, okay.
00:06:35.000
We have the optional argument, which defaults to an empty hash; then the keyword argument defaults to 10.
00:06:44.100
So when we call 'key:42', what should happen?
00:06:50.900
Anyone? What do I do for the tank?
00:06:57.110
The answer is like this, because the keyword arguments were binding before the keyword optional arguments.
00:07:05.320
So, this treats it as a real keyword argument, so the key will be 42.
00:07:11.610
Then we don't have the optional argument; the default value is used, so we get 42.
00:07:20.500
Yeah, it's easy, right? So, what will happen when we provide the empty hash as a keyword argument?
00:07:36.020
The answer is empty without the correct argument.
00:07:44.199
The reason is that when expanding the empty keyword has vanished.
00:07:52.740
Because it's empty, right? And then we have the only one empty hash as part of the keyword arguments.
00:08:01.630
This allows Ruby to interpret this as if it were optional arguments, so 42 is assigned.
00:08:13.150
The keyword arguments were empty, so it's kind of insane.
00:08:21.810
It works well most of the time, but we have some weird corner cases, and it's kind of confusing.
00:08:36.050
I can say it is a half-baked solution at most.
00:08:48.350
The Ruby design or the design of anything, including programming languages, is kind of difficult.
00:08:58.320
We have a lot of constraints and restrictions, and the biggest one is compatibility matters.
00:09:05.820
So is it tempting for us, the language designers, to fix the errors?
00:09:16.520
Like today, but changing existing behavior might upset existing software, and that does not make users very happy.
00:09:28.010
For example, in the age of Ruby 1.8 and 1.9, I had to introduce some instability to the language because of the Unicode support.
00:09:38.510
You have to migrate your application from Ruby 1.8 to Ruby 1.9. In some cases, this was a burden.
00:09:53.029
Some people kept using Ruby 1.8 for four years, maybe five, causing some community division.
00:10:01.480
The Python community had a similar division with Python 2 and Python 3 for more than ten years.
00:10:10.750
They declared the end of life for Python 2 in 2020, two years ahead of today.
00:10:25.010
Python 3 was released probably 15 years ago. That led to nearly 20 years of community division—quite unfortunate and a tragedy.
00:10:37.030
And then there's Perl 5/6—totally different programming language. Anyway, we had some kind of tragedy in many programming language communities.
00:10:45.390
You don't want to break your code, you know; you love to upgrade your software for your language.
00:10:51.820
But then, you don't want to fix or modify your application just to upgrade your language, while you also dislike confusing behavior.
00:11:01.550
So now, which do you hate more: the breakage or the confusing behavior?
00:11:11.590
This is the trade-off. The ability to maintain stability is very significant in the sense that even a small change could break your software.
00:11:20.390
For example, we have this 'palm' method which takes a hash as the argument.
00:11:28.830
But this hash can include 10 to 11, which we map the file descriptor, where the key is not a symbol—this cannot be the keyword arguments.
00:11:43.090
Or like this example is a very boilerplate example of creating elements of an HTML file—it's kind of a DSL.
00:11:52.900
It takes the name and the attributes, and it’s quite easy.
00:12:02.020
But if you want to add a new keyword argument, 'children', to express the children of the HTML tree.
00:12:11.610
This time, this HF is not a keyword argument, so we run into an argument error.
00:12:19.960
We could have compatibility errors unexpectedly when we try to fix anything.
00:12:27.980
So, what shall we do? We have several options. I will explain three options for keyword arguments.
00:12:35.210
The first one is keeping keyword arguments weird as they are.
00:12:41.590
The second option is to introduce something like 'keyword hash'. The third one is to totally separate keyword arguments.
00:12:51.080
Let me explain one by one. The first option is keeping keyword arguments weird as they are. There are pros and cons; it’s a trade-off.
00:13:00.390
Since we don't change anything, we won't have any compatibility issues. This is great, but we have the drawbacks.
00:13:09.590
Keeping the confusing behaviors can be amusing but it's still confusing.
00:13:18.740
The second option is introducing a 'keyword hash', which means the expanded hash arguments will be keyword arguments.
00:13:27.080
This is an example: take the keyword arguments. In the document list, you have keywords and not keywords.
00:13:35.770
The second form is to use the hash wrapped in braces, which would not be a document.
00:13:44.890
We will prohibit non-keyword expanded hashes as arguments, meaning you'll see an error if you do not use the proper syntax.
00:13:53.280
The difference between the current behavior and that behavior is minimal, making it easier to implement and the transition easier.
00:14:02.570
This is simpler argument delegation, which means delegating keyword arguments to different methods.
00:14:10.470
Currently, we do argument plotting and take the blocks as arguments, then delegate these arguments and blocks.
00:14:18.599
But with separate keyword arguments, we would need to delegate keyword arguments as well.
00:14:27.020
But, in my opinion, two things is annoying, three things are too much.
00:14:34.580
This option has compatibility issues, especially with known keywords and expanded hashes.
00:14:43.580
Any behavior change would cause errors in certain occasions, especially with expanded hashes.
00:14:49.720
Keyword hashes can leak; so the 'foo' which has its keyword hash assigned to the variable 'kW'.
00:15:01.360
Calling with that keyword hash returns one. It's confusing, but I think it's total for most users.
00:15:08.860
The biggest drawback for this option is how to optimize it. You have to allocate one hash per call when you can't use the keyword arguments.
00:15:21.070
But perhaps it can be room for optimization. This kind of keyword hash requires a hash object in your software; you must allocate one hash for every call.
00:15:36.000
The last option is introducing separated keyword arguments. The keyword arguments do not mix with normal arguments.
00:15:43.829
With this, we prohibit using expanded hashes as keyword arguments, improving the behavior.
00:15:50.170
The advantage of this option is the most sane behavior; we can eliminate confusing behaviors of the current keyword arguments.
00:15:57.799
The behavior is more consistent, and room for optimization exists. However, it has significant drawbacks.
00:16:02.700
The compatibility issues and the behavior change will be major when we select this option.
00:16:10.390
The argument delegation must be altered accordingly.
00:16:15.410
So there you have three options: keep keyword arguments weird; introduce keyword hash; or separate keyword arguments altogether.
00:16:23.750
Do you understand the three options? Yes?
00:16:31.800
Okay, think about them for 30 seconds.
00:16:38.260
These are the four options: keep the current behavior unchanged; introduce a keyword hash for distinguishing hashes as keyword arguments.
00:16:47.180
Finally, the third option is to separate keyword arguments altogether.
00:16:56.330
The trade-offs are as follows: the first option allows us to not change anything to maintain compatibility but keeps the confusing behavior.
00:17:03.050
The second one solves many of the confusing behaviors but is somewhat primitive.
00:17:10.120
The third option is more sophisticated and complicated to implement, but it offers optimization potential.
00:17:20.800
These three options are essential to consider.
00:17:28.680
I’d like to hear your opinion! If you prefer the first option, raise your hand!
00:17:36.580
If you prefer the first option: unchanged keyword arguments, raise your hand!
00:17:42.240
Maybe a third of the users are interested? Or more?
00:17:51.430
Thank you! What about the second option?
00:17:59.520
One, two, three! A very few votes, not more than 10%.
00:18:04.650
Okay, how about the last one? Raise your hand!
00:18:12.890
Most of you like the changes. I expected you to be courageous in this matter.
00:18:22.310
Thank you! This input is valuable for me.
00:18:35.060
In summary, compatibility is pretty important. If you use your language or library, you know how third-party things can break compatibility.
00:18:43.150
You'll be punished for no reason, and that's quite a pain, so don't do that unless you have a very strong reason.
00:18:56.460
Changes are tempting, but it might tempt me to break the language every day.
00:19:07.510
I feel I can improve the Ruby language significantly, but I don’t want to impose that on you.
00:19:16.920
Compatibility is about designing how you build your software. I design my language for you.
00:19:29.300
You are users of my language. You use it in many gems and applications. It's a layered structure.
00:19:43.300
The higher layer authors cannot punish their users for no reason. It might be tempting, but don't do it without valid reasons.
00:19:56.640
Though we have constraints, restrictions, and historical reasons for the weird keyword arguments, we are still committed to fixing those.
00:20:05.360
We can address the keyword weirdness by understanding the drawbacks and providing migration paths to the future.
00:20:18.320
We'll keep moving forward, as Ruby is not just a language; it's a community consisting of users and frameworks.
00:20:34.670
Without users, Ruby cannot be Ruby—it's all about you.
00:20:41.290
If no one uses Ruby, there is virtually no Ruby at all.
00:20:53.580
Therefore, the Ruby community has to provide attractions. We need to make our language better.
00:21:01.910
We need to attract people with tools, gems, frameworks, use cases, or posts. It’s crucial.
00:21:13.740
Punishing users with compatibility breaks can drive away motivation.
00:21:23.420
Conversely, positive changes can attract interest to a better language.
00:21:37.240
This is a challenging forecast, but we will do our best to move forward, striving for better tools.
00:21:49.829
Play with Ruby, find the weird things, keep Ruby weird.
00:21:57.230
In conclusion, I declare that I will keep Ruby users weird.
00:22:21.480
Thank you, Matz! I enjoyed your presentation. I like the ideas around changing the hashes.
00:22:29.360
I think that all of them have very good arguments.
00:22:38.740
I believe we have some time for questions. Does anyone want to grill Matz on stuff like his production code?
00:22:45.370
Yes? You over at the opposite side of the room?
00:22:52.300
Can you talk about an interesting breaking change that you wanted to make but haven’t?
00:22:59.920
One thing I abandoned years ago was changing the scope of local variables in Ruby.
00:23:06.900
Local variable scope is limited to the block, the method, or the class in which the variable is first assigned.
00:23:15.770
In many cases, you want to use the local variable out of the block.
00:23:23.840
For example, you find an object in a loop, and you want to use that object afterward.
00:23:34.280
In that case, in current Ruby, you have to assign the local variable first. Then you have to assign it in the loop.
00:23:42.920
I tried to introduce scope hoisting, like in Java or JavaScript, so you could declare a block variable outside.
00:23:55.180
But upon further consideration, that idea became confusing.
00:24:03.320
So, I abandoned that idea. I keep a long list of these kinds of crazy ideas.
00:24:09.810
Many are abandoned.
00:24:14.780
Thank you! Making that change was too big a scope?
00:24:22.230
No, not at all! It was just a bit tricky.
00:24:27.050
Any more questions? Yes, we have some time, so preferably somebody from the corner.