Project

General

Profile

Feature #3680

Splatting calls to_ary instead of to_a in some cases

Added by tmat (Tomas Matousek) over 8 years ago. Updated almost 8 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:31676]

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, #]
:to_a
#
:to_ary
#
nil
:to_ary
[#, nil]
=end

History

#1

Updated by runpaint (Run Paint Run Run) over 8 years ago

=begin
I addressed this as a footnote of http://ruby.runpaint.org/variables :

<>

So, in your example #to_a is sent when the splat operator is used explicitly; #to_ary when it is used implicitly.
=end

#2

Updated by tmat (Tomas Matousek) over 8 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

#3

Updated by runpaint (Run Paint Run Run) over 8 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

#4

Updated by tmat (Tomas Matousek) over 8 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

#5

Updated by runpaint (Run Paint Run Run) over 8 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

#6

Updated by tmat (Tomas Matousek) over 8 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
#
:to_ary
:to_a

So why should splatting, which is essentially a conversion to an array, be any different?
=end

#7

Updated by runpaint (Run Paint Run Run) over 8 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

#8

Updated by tmat (Tomas Matousek) over 8 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

#9

Updated by runpaint (Run Paint Run Run) over 8 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:in initialize'
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

#10

Updated by naruse (Yui NARUSE) over 8 years ago

  • Status changed from Open to Rejected

=begin

=end

Also available in: Atom PDF