ruby picture

RCR 115: Allow *array expansion anywhere in list

Submitted by legacy (Mon Sep 09 03:09:55 UTC 2002)

Abstract

This is a legacy RCR from Ruby Garden, submitted by jcg. Matz has declared these RCRs obsolete, and asked that their authors resubmit them in the new format.

When calling a method or creating an array, you can expand the contents of an array into the list using an asterisk (*), e.g.:

foo = [1, 2, 3, *[4, 5, 6]]

would assign the array [1, 2, 3, 4, 5, 6] to foo, and:

foo(1, 2, 3, *[4, 5, 6])

would call the method foo with the parameter set (1, 2, 3, 4, 5, 6).

This is all fine and good, but as it stands, the * only works for the last item in the list. This makes sense for method definitions, but for method invocations and array literals you should be able to use * to expand arrays at any point in the list, like so:

foo = [1, 2, *[3, *[4], 5], 6, *[7, 8, 9]]
foo(1, 2, *[3, 4], 5, *[*[6], 7])

There shouldn't be any ambiguity or other problems with this in method calls and string literals. *args in a method definition should still only be valid at the end of its parameter list, for obvious logistical reasons.

Problem

(RCR imported from old format)

Proposal

Analysis

(RCR imported from old format)

Implementation

(RCR imported from old format)
ruby picture
Comments Current voting

Why? (cout, 2002-09-09 12:12:02)

What advantage does:
foo = [1, 2, *[3, *[4], 5], 6, *[7, 8, 9]]
give over:
foo = [1, 2] + ([3] + [4] + [5]) + [6] + [7, 8, 9]

Consistency (jcg, 2002-09-09 13:22:38)

If * works on the last element, it's surprising to me that it doesn't work on other elements, which of course violates the Principle of Least Surprise that Ruby holds so dearly.

Also, method calling (jcg, 2002-09-09 13:57:06)

Right now, if you need to expand an array in the middle of a method's argument list, you need to do something like this:
tmp = [1, 2, 3] + some_array + [4, 5, 6]
method(*tmp)
I hope you agree with me that this:
method(1, 2, 3, *some_array, 4, 5, 6)
would be much nicer.

You're probably right about the limited utility of doing this in array literals, but then again there's no reason not to make array literals behave like this as well :-)

PONSA (dblack, 2002-09-10 07:40:29)

No, it only violates the Principle of Never Surprising Anyone, which is very different from POLS :-)

I like your idea, though. I wonder whether there's a parsing issue of some kind.

Implementation (czth, 2002-11-18 14:56:08)

I've actually taken a look at how this could be implemented (i.e. what changes would be needed in parse.y / eval.c), and what's come out is that Ruby's internals are somewhat crufty. Unfortunately, it seems that one needs to know Japanese to participate in ruby-dev, which puts us English-speakers at a distinct disadvantage (eek, tried the "E"nglish translation button on a ruby-dev message and the translation started "It is cotton.").

Anyway. Currently the restriction of *-expansion to the end of an array is because the node tree is built by inserting a "NODE_ARGSCAT" before ("above") the "NODE_ARRAY" (ref: parse.y calls to arg_concat which creates said node); when eval'd, NODE_ARGSCAT simply evals its params and does an rb_ary_concat. IOW, it's a hack, and Matz is no Larry.

What I see as the solution is to create a two new nodes, NODE_XARRAY (extended array) and NODE_XELEMENT (expand element). When eval'd, XARRAY will eval its children, behaving as ARRAY except for NODE_XELEMENTs, which it will concat rather than push. Thus [1,*a,3] becomes NODE_XARRAY -> (1) -> NODE_XELEMENT (a) -> 3. My reason to keep NODE_ARRAY around is for speed; knowing the number of elements beforehand can be more efficient (take a look at the C code, e.g. in eval.c's SETUP_ARGS macro); however it's probably quite possible to code NODE_ARRAY adaptively and only be slower for *-processing, which would be a superior solution, IMO.

parse.y will also need to be changed, but this actually simplifies things as (on the rhs) *a (tSTAR arg) is no longer a special case restricted to be last.

While we're on the topic, the NODE_ZARRAY seems fairly redundant, in that it should be just a NODE_ARRAY with zero elements (#define NEW_ZARRAY rb_node_newnode(NODE_ARRAY,0,0,0) in node.h), but maybe it's done for speed reasons; I hope so.

So who do I talk to about this? comp.lang.ruby gave me no joy. How about an English fork of Ruby? (HHOS)

czth


Strongly opposed 0
Opposed 0
Neutral 0
In favor 0
Strongly advocate 0
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .