00:00:08.599
Hello, good afternoon. Let's begin this almost final session for today. I think everyone started after yesterday's karaoke.
00:00:15.720
I will try to make it as fun as possible and not boring. We are talking about a cult called Mixology, but we're not going to drink today; instead, we are going to mix some concepts into Ruby.
00:00:29.359
Okay, let's go. I would like to start with a question: Why is Ruby so impressive? Why does it impress people when they see it for the first time, not just engineers?
00:00:39.399
The answer to this question lies in the very DNA of the language, and we can find it in the definition on the official website. One aspect is the elegant syntax that Ruby provides for readability and writability, which makes the language impressive.
00:01:01.480
However, Ruby's syntax and Ruby itself didn't come out of nowhere. It is a result of thoughtful work and inspiration. Yukihiro Matsumoto invented Ruby because, as I believe, he was not satisfied with the existing tools and languages.
00:01:20.799
It was also about the joy of problem-solving—designing a language that would be pleasant to use has always been, and still is, quite a challenge, and we are all trying to solve it.
00:01:39.520
Ruby has traces to other languages, mostly conceptually, semantically, and syntactically, from Smalltalk and Perl, but Ruby brought a lot of new features to the world of programming languages and continues to evolve on its own.
00:02:04.720
The syntax is a part of this evolution. If we take a look at the last ten years of Ruby since I started actively using the language, we have had many changes, both small and significant.
00:02:30.840
I think 2019 was the most fruitful year for Ruby's syntax introduction. Not all of them made it to the final release, unfortunately, but there were good attempts, and we will probably return to those issues later.
00:03:07.560
We continued adding new syntactical features, small and big, and even in the next upcoming release, we will already have one minor but really cool addition: an implicit argument for procs.
00:03:25.280
I would like to talk about what’s coming after that, because the evolution is not stopping here. We are going to have something new in the next 10, 20 years, whatever we live to see, and Ruby will thrive.
00:03:36.439
They are not going to stop, because language evolution is inevitable, and syntax evolution is a part of it. Why is that? The answer is quite simple: if a language is not evolving, it joins the list of dead languages, and we don’t want that fate for Ruby.
00:03:51.560
So there will be something new, but the pace of this evolution may vary. It could look like we introduce some new features every year, perhaps every Christmas.
00:04:11.240
It’s interesting how and why sometimes we have a lot of new features, and sometimes just a few. For many years, we may use the same syntax as before. Let’s take a look at some historical data and see how some of the features made it to release and when they were proposed.
00:04:38.280
For example, there are features that had been aging for a few years; not all that long. Some features waited for more than five years to finally reach Ruby, while the last ones were proposed and merged within the same year.
00:04:59.560
It’s interesting how the core team and everyone involved in this process reason about adding new syntax when it is a good time to do that, and why it doesn’t happen as soon as someone proposes a feature.
00:05:20.240
Introducing new syntax is not an easy task because syntax changes are irreversible. Once new syntax is added, it cannot be removed from the language. It’s really difficult.
00:05:41.320
We have flip-flop operators too; we tried to remove them, but they are still there, and I think they are going to be around for a while. To introduce new syntax, we need to clearly understand that it will bring more benefits than conceptual complexities.
00:06:01.080
Every time we introduce something new, it adds to the conceptual complexity of the language. So the question is: how do we figure out if a particular syntax proposal is going to be useful or not without even being able to use it?
00:06:26.800
When the proposal is still in draft form, it may just exist as text in an issue tracker or perhaps a branch somewhere in a fork, making it hard for regular users to use this new feature and provide feedback.
00:06:43.760
Thus, the first thing we need to do when evaluating new syntax proposals is to bring more people to experiment with them, to find corner cases, and to share their use cases and examples.
00:07:00.280
This would likely convince the core team and Matsumoto to finally merge the feature. We will see some examples of that, but how do we bring proposals to the community?
00:07:21.960
I have the answer: it's called Ruby Next. Ruby Next is a transpiler and tool for Ruby, and it's an award-winning software here in Japan. It helps to use modern Ruby features and upcoming features even on older versions or alternative implementations.
00:07:38.160
It supports Ruby as far back as 2.2, at least officially. More importantly, it gives you machinery to write your custom transformers or rewrites.
00:07:49.720
You can experiment with new syntax that is not yet available in CRuby or not yet supported by the parser—whatever parser we might use today. Since this year, Ruby Next also comes with a companion project called Rubex Playground.
00:08:13.480
It’s an online code editor specifically designed to work with transpiling Ruby code. It allows you to write Ruby code, define a rewriter for that, and then see how it behaves.
00:08:33.480
You can write simple code using regex; it’s sufficient for experimentation, and of course, you can run this code, transpile it, and see how it looks in your version of Ruby.
00:08:48.279
Everything works, and you probably already guess how it operates: it is built with Ruby wasm, so this talk is yet another talk about Ruby wasm at this conference.
00:09:03.040
I think it's worth naming this edition of RubyKaigi as 'Ruby wasm Ki'. We will use Ruby Next Playground for interactive demos during this talk.
00:09:26.160
Now, let me introduce myself. My name is Vladimir Dementyev.
00:09:35.839
If you find my badge or meet many people here, you can use my GitHub handle, which is Palcon. I maintain many projects in Ruby that you probably have heard about. I work at a company called Evil Martians.
00:09:56.600
Our Osaka team is here, and I’m happy to share whatever you want to learn about Evil Martians—why we are considered 'evil' and what we are doing here in Japan.
00:10:08.240
I have a book here, and what a coincidence! It's a book on Rails. It's currently not available in Japanese, but I hope it will be soon. If you can ask me anything about it, I think I may have a spare copy to share.
00:10:24.720
That's all about the advertisement block. Let's move on to the actual talk. As I'm trying to drink some water, the bottles on the slide and on the one I shared on Twitter depict many different programming languages.
00:10:44.120
I suggest you take a look and try to figure out if you recognize all of them; you might discover something new—that's pretty funny.
00:11:02.960
So, as we've discussed, one way to improve the efficiency of accepting or rejecting syntax proposals is to get more feedback by making these proposals accessible to users.
00:11:20.000
Another option is to learn from other languages. Nowadays, it's hardly possible to find a Ruby developer who isn't also using a second or third language.
00:11:37.200
We all write software that relies on many stacks and platforms, and we would sometimes love to see features from other languages implemented in Ruby.
00:11:53.160
For instance, I already mentioned a shorthand for hashes and keyword arguments, which dates back a long time. Initially, this was quickly rejected by Mats because Ruby did not need it.
00:12:09.120
However, years later, the position changed to him being more open-minded about it, and as a result, it was added largely due to community insistence for this feature, influenced by common use cases in JavaScript.
00:12:25.600
I’m glad this feature was made available—I have been using it for years because it has been supported in Ruby Next.
00:12:43.040
Okay, enough history. Let's look at the future and start mixing some things from other languages into Ruby.
00:13:03.920
The first language I want to talk about is PHP. This is actually how I came up with the idea for this talk. I was programming in PHP before I found Ruby, about 12 years ago—it was a decent language.
00:13:20.200
I hadn’t followed its evolution for years, but then I discovered a video showing how PHP has evolved. PHP is not dead yet; it is also evolving.
00:13:45.200
One thing that caught my attention is the operator they call the 'null coalescing assignment'. The idea is that if the left-hand side is null, we assign it to the right variable.
00:14:04.560
This is the PHP version of this feature, and other languages have similar functionality, like the Elvis operator in Kotlin, which is a bit different but serves a similar purpose.
00:14:33.920
JavaScript also has a version of null coalescing assignment. It's not surprising that we already have a proposal for this in the Ruby tracker, which is quite old and still open.
00:14:50.000
It seems it should be rejected based on the discussions, as there hasn't been enough rationale to add this feature to Ruby. However, I've been thinking: maybe we can take the idea of this operator and make it smarter.
00:15:16.760
We could justify its addition if we can think of ways it could be more useful in Ruby. Before doing so, I'd like to share a new syntax checklist with you.
00:15:41.240
First, it should not conflict with the current Ruby implementation and should raise a syntax error. This is how we gradually avoid complicating our language.
00:16:04.880
Secondly, we must justify the addition of the feature and the complexity it brings. Complexity could come not only from the semantics but also from the implementation.
00:16:29.600
The maintenance of features throughout the life of Ruby is what's truly important. It should be easy for the core team to understand and support in case the initial proposer decides not to support it.
00:16:52.000
After considering how we can introduce null coalescing assignment in Ruby, I came up with this new version of the syntax: let’s call it 'questionable assignment'.
00:17:11.160
Other languages might use two question marks, but I decided to go with just one because having too many questions in the syntax can cause confusion.
00:17:26.160
In this case, the syntax is not supported—of course, it raises a syntax error. This is a static version to show, for example, how one can assign false.
00:17:39.760
If something is assigned, it cannot be changed anymore in this way. This static example does not fully demonstrate the feature, so let's move to the Rubex Playground.
00:17:56.480
This is a working version of this operator built with Ruby Next. We can observe that we can assign false, and if we try to assign one to it, nothing will happen.
00:18:14.120
If we use our regular conditional assignment, it works as expected. This is the primary use case for operation with potentially falsy values.
00:18:31.120
However, frankly speaking, I don’t think that’s enough reason to implement this feature in Ruby. It presents a limited use case, so I started looking for more opportunities.
00:18:54.760
While reading a discussion on the Ruby tracker, I found that what the original request was for—the ability to set a hash key if it isn't defined—led to a similar proposal called 'hash fetch set.'
00:19:17.760
This concept hasn't yet made it into Ruby. So I thought, why not allow this operator to become a bit smarter and context-sensitive?
00:19:34.760
If we know that there is a hash key assignment, we can check if it's set and then update it. We could allow nil values inside, and it would work.
00:19:52.239
Another interesting use case is using it with instance variables, which can lead to code patterns relying on checking if an instance variable is defined or not.
00:20:12.480
I use this pattern frequently and would love to employ a shorter syntax for it. Let’s view this concept in action; it works.
00:20:33.040
When assigning to current user potentially being nil, by using this operator instead of the regular assignment, the method is only called once if it’s nil.
00:20:52.360
However, there's a problem: the Ruby performance team has introduced a new concept called Object Shapes. They encourage avoiding patterns in code for performance optimization.
00:21:11.280
I don’t like the idea of rewriting well-established Ruby patterns for the sake of performance tweaks that may or may not yield tangible benefits.
00:21:28.680
Nonetheless, I don't think that will be the primary reason this feature wouldn’t be accepted. Let’s take a brief look at how the transpiled version of this works.
00:21:45.880
As you can see, for hashes, we check for keys and for instance variables—we are doing the defined check manually.
00:22:01.480
We have some esoteric use cases; perhaps the latest one isn't entirely viable, but I'm waiting for more smarter object shapes to write Ruby code without worrying about the underlying implementation.
00:22:22.760
In terms of our checklist, we pass the syntax error check; we have some use cases, but not all of them are applicable today.
00:22:42.360
Speaking of implementation, effectively, it's a matter of a minor extension to the lexer and parser mechanics but requires careful consideration.
00:23:00.960
That was just a warming-up example. I aimed to demonstrate how we can think and experiment with new features and evaluate their potential.
00:23:12.680
Now, let’s talk about a more interesting language: Erlang, which is my second favorite language. I'm thrilled to show you some Erlang syntax during this talk.
00:23:32.640
If you've never seen it, this is how Erlang looks. This is a popular learning book about Erlang, which is one of the best programming books I’ve ever read, even better than my own.
00:23:50.140
This is a canonical example of pattern matching in Erlang. We have similar pattern matching features in Ruby, and I would say it looks better in Erlang.
00:24:08.560
The expressiveness of Erlang enhances readability because we can use ranges instead of guards, making it more concise and easier to understand.
00:24:32.480
However, this isn’t the only way to write functionality in Erlang. There's an alternative version that utilizes a feature known as method overloading or multi-dispatch.
00:24:52.160
I have been writing Erlang a lot, and I really appreciate this feature. I would love to see something like this in Ruby as a natural evolution of the existing pattern matching feature we’ve had.
00:25:15.760
Indeed, there is already a proposal for this feature. I believe it has the most detailed specifications, but it introduces a new keyword like 'defp,' which resembles something from other languages.
00:25:30.440
I don't like the idea of adding such non-readable keywords in Ruby, as we write our syntax in English.
00:25:47.000
There’s also a recent proposal that's somewhat related—extending endless methods to support arguments forwarding with pattern matching. It has garnered interesting discussions.
00:26:06.520
Let's return to our Ruby code and see how this could be translated into a method overloading version without needing to focus on implementation issues.
00:26:24.160
We could use existing constructs from Ruby, and the pattern on the right of it is all that’s required to implement this functionality.
00:26:42.240
While this feature isn’t supported in the latest Ruby versions, we can create a framework for its possibility.
00:26:58.800
Why do I prefer this syntax? Because it doesn't introduce unnecessary keywords, and the design of this feature resembles patterns found in other languages.
00:27:13.440
The previously mentioned proposal suggests having one 'def' statement and multiple 'end' statements, but I find it detracts from readability.
00:27:29.920
By having multiple 'def' method repetitions, we can maintain clarity in our method boundaries and intentions.
00:27:47.960
Let’s see how this would function. We have the same method from the Erlang book, and we can call it with different arguments and get different results.
00:28:05.840
The last clause catches any unmatched cases, resulting in a method error when incorrectly called. This behavior aligns perfectly with what we want to achieve.
00:28:23.360
We can also use keyword arguments quite naturally, and now let’s see if we use the common pattern from functional languages.
00:28:41.920
We can enhance this implementation to avoid redundancy by creating a definition for arguments, promoting reusable patterns.
00:28:58.960
With the current implementation, we can support positional arguments and keyword arguments, but we do not yet have combined support for both.
00:29:15.440
The question remains: do we need to make the syntax more complex to allow mixed arguments?
00:29:32.160
If you’re using method overloading, you should probably stick to one or the other—either positional or keyword arguments.
00:29:49.520
Another challenge is the lack of guards, but I believe Ruby is already expressive enough to bypass them, thus not adding extra complexity.
00:30:08.960
The advantages of pattern matching methods overloading blend seamlessly with the Ruby object model features such as inheritance.
00:30:24.800
For instance, we could inherit a class with overloaded methods while allowing for flexible use through patterns.
00:30:44.320
However, if we plan to introduce this to Ruby, we need to think thoroughly about method name clashes and how to traverse method lookup mechanisms.
00:31:03.680
For example, we could establish two versions of the same functionality through case statements and overloaded methods.
00:31:29.680
It turned out that under the hood, both versions could compile to the same byte code, retaining the efficiency of Ruby's mechanisms.
00:31:47.360
Now with Ruby 3.3, we can facilitate this restructuring for specific implementations. With all the considerations, it looks promising.
00:32:05.440
Before I wrap up, let’s mix in several additional concepts in Ruby during the remaining time. This may feel intense, but let's finish strong.
00:32:17.920
First up, there has been a push to allow the setting of instance variables directly in method definitions to reduce boilerplate code.
00:32:34.720
Many libraries attempt to simplify this initialization process without needing extensive initializers listing instance variables.
00:32:51.680
The initial proposal on this feature was made 11 years ago. Popular languages like CoffeeScript and Crystal have adopted similar strategies.
00:33:06.720
One typical example that could be modified with this feature would simplify setting up instantiation through keyword arguments.
00:33:23.920
For instance, we could utilize keyword arguments directly for initializing instance variables, enhancing code clarity.
00:33:38.240
Further limiting such functionality to the initializer methods could help tighten control and prevent redundant complexity in other methods.
00:33:53.680
Now, on to an interesting proposal emerged about anonymous structs—it has been pending for 4 years without success due to notation challenges.
00:34:11.040
Research shows that Zig has made strides in this area, emphasizing a focus on immutable structures versus Ruby’s current open struct implementations.
00:34:32.080
In conclusion, I added Elixir to my title, but we've not discussed it yet. The most wanted feature the Ruby community has sought is the pipe operator.
00:34:50.080
This discussion arose just days ago, leading to multiple implementations being suggested. The discussion is evolving, so I urge you to work on it.
00:35:11.480
There was a past proposal for a pipe operator, but it did not gain traction. The new ideas around this feature will potentially push its acceptance.
00:35:31.920
This could provide a means of translating Ruby code into a cleaner representation, giving us valuable functionality with just the right implementation.
00:35:45.920
To summarize, I urge everyone to be proactive in syntax proposal discussions, especially if you can provide examples from your libraries or applications.
00:36:04.800
If you can highlight confusion or edge cases not yet covered by the core team, this will greatly benefit the future of Ruby’s syntax.
00:36:26.840
I have compiled everything I talked about today into a single Rubex example, which can be viewed in an interactive version. There's a short link available for you to open it.
00:36:44.920
It contains many rewriters, and although it's not optimized for performance, it works well. That’s all for today. Thank you very much.
00:37:02.800
I appreciate the support from my colleagues for providing Japanese subtitles, which I hope have helped everyone understand my message today.
00:37:18.280
Thank you!