[ruby-core:109752] [Ruby master Feature#18959] Handle gracefully nil kwargs eg. **nil
From:
"LevLukomskyi (Lev Lukomskyi)" <noreply@...>
Date:
2022-08-29 00:40:51 UTC
List:
ruby-core #109752
Issue #18959 has been updated by LevLukomskyi (Lev Lukomskyi).
> You need to fail when someone passes you garbage (and `nil` is a garbage value in this case), or you need to ensure better default values (e.g., `{}`) at your call sites.
When you pass `nil` you make less allocations, also you don't need to put `|| {}` at all calling sites. For an options which are just passed somewhere else inside the method it's a valid usage.
> I suspect that it can’t be optimized easily, because arbitrarily complex expressions can be specified after `if` or `unless`.
I think it doesn't matter how complex condition there is, because the interpreter must fully evaluate it first anyway to check whether `if` is satisfied, then if the expression inside `if` is `{...}` and `**` operation is pending – just store entries in stack, not heap, and pass them to `**`. One more optimization could be to avoid creating an empty hash on `**nil`.
> It’s present of the same reason that `#to_ary` and `#to_a` are present.
Actually yeah, I see now:
```ruby
nil.to_a #=> []
nil.to_ary #=> NoMethodError: undefined method `to_ary' for nil:NilClass
nil.to_s #=> ""
nil.to_str #=> NoMethodError: undefined method `to_str' for nil:NilClass
nil.to_i #=> 0
nil.to_int #=> NoMethodError: undefined method `to_int' for nil:NilClass
nil.to_h #=> {}
nil.to_hash #=> NoMethodError: undefined method `to_hash' for nil:NilClass
```
so that's consistent. But anyway, you get used to good things quickly, I mean `*nil` somehow calls `to_a` and not `to_ary`, despite `to_ary` is designed for implicit coercions.
> I think that it’s *generally* a better change for the examples you have provided than `**nil` support.
Actually that's not consistent that you are ok for `merge(nil)` but not ok for `add_email_to_list(tracking_info: nil)` which is kind of the same.
> I’m still not fond of the pattern, and think that an explicit call to `Hash#delete_if` to remove defaulted values is clearer and more intentional, but whatever.
I don't think that the way with `delete_if` is clearer since you mentally are doing two operations – first insert values into hash, and then remove them. In case of `**` you are doing only one operation – insert values.
> Starting with Rails 6.1 you can do:
>
> ```ruby
> content_tag(:div, 'Content', class: token_list('is-hero': hero, 'is-search-page': search_page))
> ```
Actually this generates `<div class="">Content</div>` if all values are `false`, so `.presence` is still needed 😢 But yeah, good to know that there is this way 👍
----------------------------------------
Feature #18959: Handle gracefully nil kwargs eg. **nil
https://bugs.ruby-lang.org/issues/18959#change-98989
* 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>