[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Where is splat implemented? / How does it work?

Mischa Fierer

2/25/2009 1:47:00 PM

Hi --

After a good amount of digging, I'm still not sure how [*[1,2]] ==
[1,2]. I understand what it does, but I want to know why.

I had originally assumed it was a normal method (like + or -), but it
appears to be implemented at a deeper level. It looks like rubinius does
something with cast_array. Ruby 1.8 does to_ary and ruby 1.9 does
to_splat. Where does this happen? to_ary says that it just returns self,
so how does self get exploded?

Short of reading eval.c, does anyone have any pointers?

Best,

Mischa
--
Posted via http://www.ruby-....

11 Answers

David A. Black

2/25/2009 2:37:00 PM

0

Hi --

Mischa Fierer wrote:
> Hi --
>
> After a good amount of digging, I'm still not sure how [*[1,2]] ==
> [1,2]. I understand what it does, but I want to know why.
>
> I had originally assumed it was a normal method (like + or -), but it
> appears to be implemented at a deeper level. It looks like rubinius does
> something with cast_array. Ruby 1.8 does to_ary and ruby 1.9 does
> to_splat. Where does this happen? to_ary says that it just returns self,
> so how does self get exploded?
>
> Short of reading eval.c, does anyone have any pointers?

It ties itself to to_a:

>> obj = Object.new
=> #<Object:0xa43db54>
>> def obj.to_a; [1,2,3]; end
=> nil
>> a = *obj
=> [1, 2, 3]

That code works the same in 1.8 and 1.9.1. There may be some classes
where it's optimized away so that you can't override it even by defining
to_a. I'm not sure.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.r...
Coming in 2009: The Well-Grounded Rubyist (http://manning....)

Ruby Training Atlanta! April 1-3, http://www.entp.com/training...

Mischa Fierer

2/25/2009 2:52:00 PM

0

David,

Thank you for your reply.

However, what I'm wondering is, where does it "tie" itself to
to_ary/to_a? Also, I think it is not the same thing as to_a, when used
like so:

>> [[1,2,3].to_a]
=> [[1, 2, 3]] # outer length = 1
vs
>> [*[1,2,3]]
=> [1, 2, 3] # length = 3
>>

Or

>> str = "asdf"
>> def str.to_a
>> ["oops"]
>> end
=> nil

>> [*str]
=> ["oops"]

vs

>> [str.to_a]
=> [["oops"]]

I'm really curious where / how this happens. If anyone can point me to
either rubinius or to mri, I would be very grateful.


M


David A. Black wrote:
> Hi --
>
> Mischa Fierer wrote:
>>
>> Short of reading eval.c, does anyone have any pointers?
>
> It ties itself to to_a:
>
> >> obj = Object.new
> => #<Object:0xa43db54>
> >> def obj.to_a; [1,2,3]; end
> => nil
> >> a = *obj
> => [1, 2, 3]
>
> That code works the same in 1.8 and 1.9.1. There may be some classes
> where it's optimized away so that you can't override it even by defining
> to_a. I'm not sure.
>
>
> David
>
> --
> David A. Black / Ruby Power and Light, LLC
> Ruby/Rails consulting & training: http://www.r...
> Coming in 2009: The Well-Grounded Rubyist (http://manning....)
>
> Ruby Training Atlanta! April 1-3, http://www.entp.com/training...

--
Posted via http://www.ruby-....

Rick DeNatale

2/25/2009 3:23:00 PM

0

[Note: parts of this message were removed to make it a legal post.]

On Wed, Feb 25, 2009 at 9:37 AM, David A. Black <dblack@rubypal.com> wrote:

> Hi --
>
> Mischa Fierer wrote:
>
>> Hi --
>>
>> After a good amount of digging, I'm still not sure how [*[1,2]] ==
>> [1,2]. I understand what it does, but I want to know why.
>>
>> I had originally assumed it was a normal method (like + or -), but it
>> appears to be implemented at a deeper level. It looks like rubinius does
>> something with cast_array. Ruby 1.8 does to_ary and ruby 1.9 does
>> to_splat. Where does this happen? to_ary says that it just returns self,
>> so how does self get exploded?
>>
>> Short of reading eval.c, does anyone have any pointers?
>>
>
> It ties itself to to_a:
>
> >> obj = Object.new
> => #<Object:0xa43db54>
> >> def obj.to_a; [1,2,3]; end
> => nil
> >> a = *obj
> => [1, 2, 3]
>
> That code works the same in 1.8 and 1.9.1. There may be some classes where
> it's optimized away so that you can't override it even by defining to_a. I'm
> not sure.
>

I'm pretty sure that implicit conversions to array use to_ary, not to_a.
This is the way it works in Ruby 1.8, and after an experimental period in
1.9 with to_splat, it appears to have gone back to using to_ary. Grepping
the Ruby 1.9.1 source code for to_splat shows no hits.

To answer Mischa question about how it "ties itself," this is done
internally by the Ruby interpreter/VM when it needs to implicitly convert
the value of a *expression when it appears either as an actual parameter to
a method call, or on the right hand side of an assignment.

So it can't be explained completely at the ruby source code level, it's part
of the semantics of the language.

--
Rick DeNatale

Blog: http://talklikeaduck.denh...
Twitter: http://twitter.com/Ri...
WWR: http://www.workingwithrails.com/person/9021-ric...
LinkedIn: http://www.linkedin.com/in/ri...

-lim-

2/25/2009 3:43:00 PM

0

>>> [*str]
> => ["oops"]
>
> vs
>
>>> [str.to_a]
> => [["oops"]]
>
> I'm really curious where / how this happens. If anyone can point me to
> either rubinius or to mri, I would be very grateful.

If you do grep -i splat *.c, you'll find hits in parse.c and compile.c
(for ruby 1.9.1).

I'd assume it's a mostly parser thing. That to_a comes into play
probably it because it makes sure it's an array it is unsplicing --
just an assumption.

aldric[removeme]

2/25/2009 5:34:00 PM

0

Mischa Fierer wrote:
> Short of reading eval.c, does anyone have any pointers?
>

Did this strike anybody else as really funny?

--Aldric

David A. Black

2/25/2009 9:11:00 PM

0

Hi --

Rick DeNatale wrote:
> On Wed, Feb 25, 2009 at 9:37 AM, David A. Black<dblack@rubypal.com> wrote:
>
>> Hi --
>>
>> Mischa Fierer wrote:
>>
>>> Hi --
>>>
>>> After a good amount of digging, I'm still not sure how [*[1,2]] ==
>>> [1,2]. I understand what it does, but I want to know why.
>>>
>>> I had originally assumed it was a normal method (like + or -), but it
>>> appears to be implemented at a deeper level. It looks like rubinius does
>>> something with cast_array. Ruby 1.8 does to_ary and ruby 1.9 does
>>> to_splat. Where does this happen? to_ary says that it just returns self,
>>> so how does self get exploded?
>>>
>>> Short of reading eval.c, does anyone have any pointers?
>>>
>> It ties itself to to_a:
>>
>>>> obj = Object.new
>> => #<Object:0xa43db54>
>>>> def obj.to_a; [1,2,3]; end
>> => nil
>>>> a = *obj
>> => [1, 2, 3]
>>
>> That code works the same in 1.8 and 1.9.1. There may be some classes where
>> it's optimized away so that you can't override it even by defining to_a. I'm
>> not sure.
>>
>
> I'm pretty sure that implicit conversions to array use to_ary, not to_a.
> This is the way it works in Ruby 1.8, and after an experimental period in
> 1.9 with to_splat, it appears to have gone back to using to_ary. Grepping
> the Ruby 1.9.1 source code for to_splat shows no hits.

to_ary doesn't seem to be involved in 1.9.1. If you swap to_a for to_ary
in my example it comes out the same in 1.8.6, but in 1.9.1 it doesn't:

>> obj = Object.new
=> #<Object:0x93413c8>
>> def obj.to_ary; [1,2,3]; end
=> nil
>> a = *obj
=> [#<Object:0x93413c8>]


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.r...
Coming in 2009: The Well-Grounded Rubyist (http://manning....)

Ruby Training Atlanta! April 1-3, http://www.entp.com/training...

-lim-

2/26/2009 5:27:00 AM

0

> > I'm pretty sure that implicit conversions to array use to_ary, not to_a.
> > This is the way it works in Ruby 1.8, and after an experimental period in
> > 1.9 with to_splat

Building on David Black's examples, I tried the following to clarify
the use of #to_a and #to_ary for me.

> RUBY_VERSION
=> "1.9.1"
> o = Object.new
=> #<Object:0x100af964>
> class << o
> def to_a
> [:a]
> end
> def to_ary
> [:ary]
> end
> end
> [*o]
=> [:a]
> [1] + o
=> [1, :ary]

The #to_splat method seems to be gone.

Robert Klemme

2/26/2009 8:33:00 AM

0

2009/2/26 minilith@gmail.com <minilith@gmail.com>:
>> > I'm pretty sure that implicit conversions to array use to_ary, not to_=
a.
>> > This is the way it works in Ruby 1.8, and after an experimental period=
in
>> > 1.9 with to_splat
>
> Building on David Black's examples, I tried the following to clarify
> the use of #to_a and #to_ary for me.
>
>> RUBY_VERSION
> =3D> "1.9.1"
>> o =3D Object.new
> =3D> #<Object:0x100af964>
>> class << o
>> =A0 =A0 def to_a
>> =A0 =A0 =A0 =A0 [:a]
>> =A0 =A0 end
>> =A0 =A0 def to_ary
>> =A0 =A0 =A0 =A0 [:ary]
>> =A0 =A0 end
>> end
>> [*o]
> =3D> [:a]
>> [1] + o
> =3D> [1, :ary]
>
> The #to_splat method seems to be gone.

Here's another test set

09:31:50 OPSC_Gold_bas_dev_R2.0_foundation$ ruby /tmp/o.rb
"1.8.7"
1
2
3
"--------------"
#<Object:0x1002dfcc>
nil
nil
"--------------"
5
6
7
"--------------"
5
6
7
"=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D"
1
2
3
"--------------"
error
error
5
6
7
"--------------"
09:31:55 OPSC_Gold_bas_dev_R2.0_foundation$ ruby19 /tmp/o.rb
"1.9.1"
1
2
3
"--------------"
#<Object:0x1001a3dc>
nil
nil
"--------------"
#<Object:0x1001a378>
nil
nil
"--------------"
5
6
7
"=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D"
1
2
3
"--------------"
error
error
error
09:31:57 OPSC_Gold_bas_dev_R2.0_foundation$ cat /tmp/o.rb

p RUBY_VERSION

def t(a,b,c)
p a,b,c, '--------------'
end

o =3D Object.new

def o.to_a
[1,2,3]
end

q =3D Object.new

def q.to_ary
[5,6,7]
end

a, b, c =3D *o
p a,b,c, '--------------'

a, b, c =3D o
p a,b,c, '--------------'

a, b, c =3D *q
p a,b,c, '--------------'

a, b, c =3D q
p a,b,c, '=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D'

t(*o) rescue puts "error"
t(o) rescue puts "error"

t(q) rescue puts "error"
t(*q) rescue puts "error"
09:32:04 OPSC_Gold_bas_dev_R2.0_foundation$

Cheers

robert


--=20
remember.guy do |as, often| as.you_can - without end

Mischa Fierer

2/26/2009 12:13:00 PM

0

Thanks for all the replies.

I've taken a lot of the stuff here, plus all the examples I could find
for the splat, and written what I think is probably the most definitive
guide at this point. You can run it yourself, as well, which should be
useful.

Some of the examples are from a post David Black made a year or so ago,
others are from Rick and matz.

http://github.com/mischa/splat/t...

I've also written it so that you can try to guess what the return value
will be.

Please do let me know if anything is wrong with it, also feel free to
fork and add your own examples / tests.

Best,

M

Robert Klemme wrote:
> 2009/2/26 minilith@gmail.com <minilith@gmail.com>:
>> => #<Object:0x100af964>
>>> [1] + o
>> => [1, :ary]
>>
>> The #to_splat method seems to be gone.
>
> Here's another test set
>
> 09:31:50 OPSC_Gold_bas_dev_R2.0_foundation$ ruby /tmp/o.rb
> "1.8.7"
> 1
> 2
> 3
> "--------------"
> #<Object:0x1002dfcc>
> nil
> nil
> "--------------"
> 5
> 6
> 7
> "--------------"
> 5
> 6
> 7
> "=============="
> 1
> 2
> 3
> "--------------"
> error
> error
> 5
> 6
> 7
> "--------------"
> 09:31:55 OPSC_Gold_bas_dev_R2.0_foundation$ ruby19 /tmp/o.rb
> "1.9.1"
> 1
> 2
> 3
> "--------------"
> #<Object:0x1001a3dc>
> nil
> nil
> "--------------"
> #<Object:0x1001a378>
> nil
> nil
> "--------------"
> 5
> 6
> 7
> "=============="
> 1
> 2
> 3
> "--------------"
> error
> error
> error
> 09:31:57 OPSC_Gold_bas_dev_R2.0_foundation$ cat /tmp/o.rb
>
> p RUBY_VERSION
>
> def t(a,b,c)
> p a,b,c, '--------------'
> end
>
> o = Object.new
>
> def o.to_a
> [1,2,3]
> end
>
> q = Object.new
>
> def q.to_ary
> [5,6,7]
> end
>
> a, b, c = *o
> p a,b,c, '--------------'
>
> a, b, c = o
> p a,b,c, '--------------'
>
> a, b, c = *q
> p a,b,c, '--------------'
>
> a, b, c = q
> p a,b,c, '=============='
>
> t(*o) rescue puts "error"
> t(o) rescue puts "error"
>
> t(q) rescue puts "error"
> t(*q) rescue puts "error"
> 09:32:04 OPSC_Gold_bas_dev_R2.0_foundation$
>
> Cheers
>
> robert

--
Posted via http://www.ruby-....

David A. Black

2/26/2009 12:56:00 PM

0

Hi --

Mischa Fierer wrote:
> Thanks for all the replies.
>
> I've taken a lot of the stuff here, plus all the examples I could find
> for the splat, and written what I think is probably the most definitive
> guide at this point. You can run it yourself, as well, which should be
> useful.
>
> Some of the examples are from a post David Black made a year or so ago,
> others are from Rick and matz.
>
> http://github.com/mischa/splat/t...
>
> I've also written it so that you can try to guess what the return value
> will be.
>
> Please do let me know if anything is wrong with it, also feel free to
> fork and add your own examples / tests.

Note this change, between 1.8 and 1.9:

1.8:

>> *a = [1,2,3]
=> [[1, 2, 3]]
>> a
=> [[1, 2, 3]]

1.9:

>> *a = [1,2,3]
=> [1, 2, 3]
>> a
=> [1, 2, 3]

I think this means that my "that which, when wrapped in an array"
formula doesn't work for this case any more -- though that formula was
my best shot at a pithy way of describing the star, and may be outdated
and/or incomplete in other ways. (I can't think of any, but someone else
might :-)

That's why I've always called this the "unary unarray" operator.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.r...
Coming in 2009: The Well-Grounded Rubyist (http://manning....)

Ruby Training Atlanta! April 1-3, http://www.entp.com/training...