Feature #3680
closedSplatting calls to_ary instead of to_a in some cases
Added by tmat (Tomas Matousek) over 14 years ago. Updated over 13 years ago.
Description
=begin
In some cases to_ary is called to splat an array. Shouldn't to_a be always called?
class C
def respond_to? name
p name
false
end
end
p [1,*C.new]
p(*C.new)
x,y = C.new
p x,y
proc {|a,b| p [a,b] }.call(C.new)
ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32]
:to_a
[1, #<C:0x1984aa0>]
:to_a
#<C:0x1984900>
:to_ary
#<C:0x1984820>
nil
:to_ary
[#<C:0x1984680>, nil]
=end
Updated by runpaint (Run Paint Run Run) over 14 years ago
=begin
I addressed this as a footnote of http://ruby.runpaint.org/variables :
<<Note that :to_ary is sent rather than the :to_a used by the splat operator. In the former case, the programmer did not directly request that conversion take place so the message for implicit conversion is sent; in the latter, his use of the splat operator constitutes an explicit conversion, so the more liberal protocol is followed.>>
So, in your example #to_a is sent when the splat operator is used explicitly; #to_ary when it is used implicitly.
=end
Updated by tmat (Tomas Matousek) over 14 years ago
=begin
I see, so there is implicit and explicit splatting. The former uses to_ary the latter to_a.
So the splatting in the following code (in when clause) should use to_a since it is an explicit one, right? It attempts to call both to_ary and to_a though. I would expect it to call just to_a like any other explicit splatting.
class C
def respond_to? name
p name
false
end
def to_s
'c'
end
end
case
when *C.new;
end
output:
D:\temp\RubyParser>ruby19 -v x.rb
ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32]
:to_ary
:to_a
=end
Updated by runpaint (Run Paint Run Run) over 14 years ago
=begin
I don't have a 1.9.1 installed ATM, but on trunk/1.9.3 the following does indeed call #to_a. (#respond_to? isn't used in this case, no pun intended).
class C
def to_ary; p :ary; [:ary]; end
def to_a; p :a; [:a]; end
end
case
when *C.new
end
run@paint:/tmp → ruby -v x.rb
ruby 1.9.3dev (2010-06-05 trunk 28178) [i686-linux]
:a
=end
Updated by tmat (Tomas Matousek) over 14 years ago
=begin
OK, so this seems to be fixed. I haven't found more recent binaries for Windows than 1.9.1 so I can't test that.
So you're saying when *expr doesn't call respond_to? in 1.9.3? That looks like a bug unless splatting operations in general don't call respond_to? anymore in >1.9.1. They do in 1.9.1.
=end
Updated by runpaint (Run Paint Run Run) over 14 years ago
=begin
OK, so this seems to be fixed. I haven't found more recent binaries for Windows
than 1.9.1 so I can't test that.
So you're saying when *expr doesn't call respond_to? in 1.9.3? That looks like
a bug unless splatting operations in general don't call respond_to? anymore in
1.9.1. They do in 1.9.1.
They don't in general. The object is sent #to_a, if it responds--either because it defines a method by that name or #method_missing--the return value is splatted; otherwise [self] is used.
=end
Updated by tmat (Tomas Matousek) over 14 years ago
=begin
I'm confused. You're saying that in 1.9.2+ splatting operators don't call respond_to?(:to_a/:to_ary)? Almost all other conversions do call respond_to? first before calling the method itself. E.g.:
class C
def respond_to?(name)
p name
false
end
end
String(C.new) rescue p $!
Array(C.new)
ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32]
:to_s
#<TypeError: can't convert C into String>
:to_ary
:to_a
So why should splatting, which is essentially a conversion to an array, be any different?
=end
Updated by runpaint (Run Paint Run Run) over 14 years ago
=begin
It's not. There's been a spec. change: instead of calling #respond_to?, we just try and call the method, rescuing NoMethodError. That is, #respond_to? will not be called by Array(), String(), Float(), etc. Splatting isn't special-cased. Besides, now we have #respond_to_missing?, an explicit conversion would have an overhead of two method calls... For background see revision 25556 and http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23738 .
=end
Updated by tmat (Tomas Matousek) over 14 years ago
=begin
Let's say I call File.open(C.new) where
class C
def to_str; "a.txt"; end
def method_missing name, *arg
raise NoMethodError
end
end
File.open tries at most 4 conversions: to_int, to_hash, to_path and to_str.
So if I understand this change well it means that File.open(C.new) calls method_missing(:to_int), method_missing(:to_hash) and method_missing(:to_path) throwing and rescueing 3 exceptions in order to find out that it should call to_str?
In other words there is no way how to detect that an object is convertible to_str w/o actually performing the conversion, calling to_str or method_missing(:to_str)?
=end
Updated by runpaint (Run Paint Run Run) over 14 years ago
=begin
o=Object.new
=> #Object:0x8b67d28
def o.method_missing(m,*)
p [method, m]
super
end
=> nil
File.open(o)
[:method_missing, :to_int]
[:method_missing, :to_hash]
[:method_missing, :to_path]
[:method_missing, :to_str]
TypeError: can't convert Object into String
from (irb):12:ininitialize' from (irb):12:in
open'
from (irb):12
from /usr/local/bin/irb:12:in `'
We should probably take this off-list to avoid cluttering the BTS any more; feel free to e-mail me.
=end