[ruby-core:109724] [Ruby master Feature#18959] Handle gracefully nil kwargs eg. **nil
From:
"austin (Austin Ziegler)" <noreply@...>
Date:
2022-08-27 00:21:14 UTC
List:
ruby-core #109724
Issue #18959 has been updated by austin (Austin Ziegler).
LevLukomskyi (Lev Lukomskyi) wrote in #note-19:
> > Both @sawa and I have provided better options than your examples.
>
> It looks like you guys are ashamed of `**` and try to avoid it at all costs, instead of grasping this new language feature.
Not at all. I’ve used `**` for a *very* long time (since kwargs were introduced). But I also tend to do `**(options || {})` and I don’t write hard-to-read code like `**({id: id, name: name} if id.present?)`.
> Also, I've found one more [good example](https://www.semicolonandsons.com/code_diary/ruby/double-splat-cannot-handle-nil). Here a programmer has to write this:
> ```ruby
> def add_email_to_list(email:, tracking_info:)
> EmailSubscriber.find_by(email: email) || EmailSubscriber.new(
> email: email, **(tracking_info || {})
> )
> ...
> end
> ```
> instead of just writing this:
> ```ruby
> def add_email_to_list(email:, tracking_info:)
> EmailSubscriber.find_by(email: email) || EmailSubscriber.new(
> email: email, **tracking_info
> )
> ...
> end
> ```
I’d write that as:
```ruby
def add_email_to_list(email:, tracking_info: {})
EmailSubscriber.find_by(email: email) || EmailSubscriber.new(email: email, **tracking_info)
end
```
It *is* a different signature (`tracking_info` is no longer a required kwarg) but it’s a *better* API and doesn’t require any special shenanigans.
I’m not arguing against the improvement of `**` or even permitting `**nil`. But the example you’ve given is completely unconvincing because it is suboptimal, has unnecessary allocations, and is more reminiscent of what I’d see from Perl in 1998 than Ruby of 2022.
I think that there are two things that could help:
1. If we want to allow `**nil`, the argument should be made to switch `**` from calling `#to_hash` to calling `#to_h`, because that would give you `**nil` _for free_. That would require digging a bit deeper to determine why `**` calls `#to_hash` instead of `#to_h`.
2. `Hash#merge` and `Hash#update` could be modified to ignore `nil` arguments (which would permit your use-case).
----------------------------------------
Feature #18959: Handle gracefully nil kwargs eg. **nil
https://bugs.ruby-lang.org/issues/18959#change-98959
* Author: LevLukomskyi (Lev Lukomskyi)
* Status: Open
* Priority: Normal
----------------------------------------
The issue:
```ruby
def qwe(a: 1) end
qwe(**nil) #=> fails with `no implicit conversion of nil into Hash (TypeError)` error
{ a:1, **nil } #=> fails with `no implicit conversion of nil into Hash (TypeError)` error
```
Reasoning:
I found myself that I often want to insert a key/value to hash if a certain condition is met, and it's very convenient to do this inside hash syntax, eg.:
```ruby
{
some: 'value',
**({ id: id } if id.present?),
}
```
Such syntax is much more readable than:
```ruby
h = { some: 'value' }
h[:id] = id if id.present?
h
```
Yes, it's possible to write like this:
```ruby
{
some: 'value',
**(id.present? ? { id: id } : {}),
}
```
but it adds unnecessary boilerplate noise.
I enjoy writing something like this in ruby on rails:
```ruby
content_tag :div, class: [*('is-hero' if hero), *('is-search-page' if search_page)].presence
```
If no conditions are met then the array is empty, then converted to nil by `presence`, and `class` attribute is not rendered if it's nil. It's short and so convenient! There should be a similar way for hashes!
I found this issue here: https://bugs.ruby-lang.org/issues/8507 where "consistency" thing is discussed. While consistency is the right thing to do, I think the main point here is to have fun with programming, and being able to write stuff in a concise and readable way.
Please, add this small feature to the language, that'd be so wonderful! 🙏
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>