Hi, I’m from MRI core team, and am an original proposer of this change in question. First of all, I’m really sorry for those who feel pain against this change, and thank you for taking time to share your feeling to this thread. I’d like to make effort to find the best way, with matz and MRI core team.
I summarized the pain points and added my comments. (Note that my comments are mine, not the consensus of MRI core team.) If I miss anything, sorry but let me know. And if your pain point is not shared yet, please write a comment and let us know.
There are too many warnings
there’s indeed a lot of them. (#7 byroot)
Over 50 000 warnings generated by our CI, thousands from gems. I didn’t know where to start. (#8 Florent)
various loud deprecations. (#15 samsaffron)
By going immediately noisy with deprecation warnings, they in practice become an immediate breaking change (#25 Matthew)
I totally hear you. I think we will make the warnings opt-in in 2.7.2.
Just silencing deprecations feels wrong to us as a path forward. If Ruby, by default, is being loud I feel we just have to deal with it. (#15 samsaffron)
You are right. Actually, we wanted you not to ignore them because your code would break on Ruby 3.0 (in the original plan). However, it is reasonable to tentatively suppress them when you are not responsible to fix them, e.g., in production. Thus, we also provided a new option
-W:no-deprecated and an API
Warning[:deprecated] = false to suppress the warnings, which are mentioned in the official migration guide (described later).
Note that the option stops not only keyword arguments, but also all deprecations. Shopify’s
deprecation_toolkit would be a better solution for fine-grained control of suppression. Thank you, Shopify team.
Argument forwarding is difficult
capturing args to forward them to classes later, at this point I have found a bit painful to be able to capture kwargs indeed. (#2 Thibaut)
the main pain point for our library is that we transparently pass around arguments given by users, to user code, so we are not directly aware (in a lot of cases) wether keyword arguments are being used or not (#4 Jon)
the main pain point is blind delegation (#7 byroot)
Ruby is only missing two things: An easy way to forward parameters that works on all currently supported versions. (#10 byroot)
Capturing arguments “arbitrarily” to forward them on later. (#12 Jonathan)
That’s the very point that we were worrying about.
In the original plan, Ruby 3.0 would achieve very simple world; positional arguments are consistently passed as positionals, and keyword arguments are done as keywords. However, this is essentially incompatible, so compatible argument forwarding (that works from 2.6 to 3.0) became very difficult. We took so much time to design a migration path, but we couldn’t come up with a better one than
ruby2_keywords. (At first glance, it looks like there are many better ways, but maybe they have very complex trade-offs.)
I admit that separating keyword arguments from positional ones makes “capturing args” less concise and less performant. IMO, we may mitigate some parts of the issue by introducing a new notion (and notation) of, say, Arguments class. But the design requires careful (and long) consideration, so we cannot do that right away.
FYI: matz plans to relax the current limitation of
.... It is not decided whether it is backported to Ruby 2.7, though.
ruby2_keywords is ugly
I suppose that’s what ruby2_keywords is for, but it’s tedious to use. (#7 byroot)
Firstly I am uneasy with the whole ruby2_keywords thing, it feels like I am adding technical debt to my code when I hack it in. (#15 samsaffron)
You are perfectly right. In a sense, this is by design. This is just for backward-compatible code that needs to work on Ruby 2.6…Ruby 3.0. In the future, when you use only Ruby 3.0 or later (as byroot said, this is typical for many applications), we want people to write code that completely separates positional arguments and keyword arguments without
ruby2_keywords. This intentional ugliness works too much for you, though.
Unfortunately, Ruby 2.7’s keyword arguments are “half-baked”. You MUST use
ruby2_keywords if you need argument forwarding that works perfectly in Ruby 2.7. RUBY_VERSION check or some such thing will bring you some unfortunate corner cases. See the official migration guide (described later) in detail. We tried hard to design the migration path, but I couldn’t create an elegant one. And so instead we chose this “intentionally ugly” temporary directive that would make you want you to migrate to Ruby 3 as soon as possible.
IMHO, the true sadness is a long lasting “half-baked” state. This is the reason why we rushed to introduce the defaulted-on warning into 2.7, and planned the hasty change in 3.0. I’m afraid if postponing the change will not mitigate the pain but increase the pain. Giving up the change completely may somewhat solve your pain for migrating, though this means that Ruby misses the chance to fix this essentially broken language design mistake.
Some DSL cases are difficult
In the specific case of DSL… (#2 Thibaut)
I went through the kiba DSL, and noticed that it apparently represents a calculation of
MyClass.new(r, k: 42) by using an Array literal:
[MyClass, r, k: 42]. This deeply depends upon Ruby 2 keyword semantics, so it is essentially difficult. To make it compatible with Ruby 2, you may check if the last element is a Hash (by using
Object#is_a?), and check if the target method accepts keywords (by using
Method#parameters). Personally, I’d like to recommend to use a Proc instead of an Array for such a calculation, but I admit that it may reduce value of DSL.
the “user-facing” API of my library (a robotic framework) is a DSL (#13 Sylvain)
The DSL seems to hit the change of handling non-symbol keys which is accepted as keywords. I may be wrong, but I guess it is not difficult to manually separate non-symbol keys from
**kwargs. (Of course, I’m really sorry for asking you to change your code.) I’ve commented it to Ruby bug tracker ticket.
MRI core team is less communicative
Some communication around the tools available as well as some kind of migration guide. (#10 byroot)
Whether it’s good or bad, I’ve posted a migration guide into the official Ruby site. And the article is referred by the release announcement of Ruby 2.7.0. I don’t blame you for missing (or forgetting) the article, but to be honest, it is sad for me that no one mentions it.
Fixing gem upstream is difficult
Dealing with gem warnings can be a nightmare. (#15 samsaffron)
The bigger challenge we have is around upstreaming fixes to gems, but we will certainly also get through that. (#20 samsaffron)
I agree with you. If you encounter any difficult case, please post it here. The Ruby core team members are willing to help the fix. I may not be in a position to say this, but I believe in the power of the Ruby community.
Again, thank you for all your comments. If you think your pain is not expressed yet, please add yours. I’d like to hear you.