Double splat on `nil`
Asked Answered
R

2

5

My understanding is that a single splat on a non-array object calls to_a and then dissociates the elements apart. And since nil.to_a is defined to be [], the following conversion happens:

[:foo, *nil, :bar]
# => [:foo, *nil.to_a, :bar]
# => [:foo, *[], :bar]
# => [:foo, :bar]

By analogy, I thought that a double splat on a non-hash object calls to_h and then dissociates the key-value pairs apart. And since nil.to_h is defined to be {}, I expected the following conversion to happen:

{"foo" => 1, **nil, "bar" => 2}
# => {"foo" => 1, **nil.to_h, "bar" => 2}
# => {"foo" => 1, **{}, "bar" => 2}
# => {"foo" => 1, "bar" => 2}

But actually, it raises an error: no implicit conversion of nil into Hash. Why does it behave like that?

Edit I am not asking about the reasoning behind the design. I am asking where my thinking is wrong regarding double splat.

Ramtil answered 7/12, 2015 at 3:4 Comment(0)
Y
6

Well it's our human being super power to recognize patterns and predict things. However it's not always true. This is one example. Ruby is not consistent in splat and double splat. Your way of thinking is a good way to "remember" but it's not exactly the way Ruby works on splats.

See this bug report for more detail. In this bug report, Ruby's author Matz rather to remove the feature of being able to splat nil than add double splat to nil.

Yonatan answered 7/12, 2015 at 3:41 Comment(1)
This is a good explanation - like the pointer to bug and views of Matz.Ethyl
K
1

The reason *nil works is because the splat operator works on anything that responds to to_a, and nil.to_a returns []. The reason **nil doesn't work is that nil doesn't respond to to_hash, which is to_a's double-splat counterpart.

If you wanted this behavior, you could monkey-patch NilClass:

class NilClass
  def to_hash
    {}
  end
end

{ "foo" => 1, **nil, "bar" => 2 }
# => { "foo" => 1, "bar" => 2 }
Krohn answered 7/12, 2015 at 5:13 Comment(7)
So, you are saying that not to_h but to_hash applies in double splat? I thought that placing a single splat is explicit class conversion, and hence it is to_a rather than to_ary (my understanding is that to_a is explicit, to_ary is implicit), but it looks like that part of my thinking was wrong.Ramtil
Yes, splat is implicit, not explicit conversion.Krohn
That contradicts the accepted answer to this question. Is that answer wrong?Ramtil
It also contradicts the very weblog post that you linked.Ramtil
Notice that the usage of to_a versus to_ary changed between Ruby 1.8 and Ruby 1.9, so you should not rely on old information.Ramtil
Yes, it seems that I was mistaken. I've edited my answer to be accurate, although your attitude and your edits to my answer suggest that maybe you should have just researched and answered the question yourself.Krohn
Warning: this can change the semantics of using nil as a method argument (just spent too long debugging this). Using def x(a = "default", b:2) p [a, b] end, x(nil) will throw a deprecation warning and try to treat the nil as an empty hash of keyword arguments, rather than setting a to nil.Hatley

© 2022 - 2024 — McMap. All rights reserved.