Multiple values & variable assignment

Why does
    def f; 1, 2, 3; end
    x,y,z = f
give x=1, y=2, z=3

While
    x = f
gives x=[1, 2, 3]?

Also:
    x,y=[1, 2], 3 #=> x=[1,2] and y[3], fine
    x,y=[1, 2] #=> x=1, y=2 : not fine

This is inconsistent. Instead I propose:
    x,y,z = f #=> x=1,y=2,z=3 (as current)
    x = f #=> x = 1 (CHANGE)
    *x = f #=> x=[1, 2, 3] (CHANGE)
    x, y = *f #=> an error (f on R.h.s. is not an array)
    x, y, z = [1, 2, 3], 4, 5 #=> x=[1, 2, 3], y=4, z=5 (current)
    x, y, z = [1, 2, 3] #=> x=[1, 2, 3], y=nil, z=nil (CHANGE)
    *x = [1, 2, 3] #=> x = [[1, 2, 3]] (current)
    x, y, z = *[1, 2, 3] #=> x=1, y=2, z=3 (current)

I am trying to write a getter that returns the instance variable + something
special if that instance variable has not been initialized. Normal calls
would ignore the second return value. The behavior above makes it difficult.
Alternatives? Will this continue in 2.0?

Thanks.

btw: I think arrays and multiple values should be strictly separated
syntactic classes.

"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag news:WkJDd.9898$ho.9719@fe2.texas.rr.com...

Why does
   def f; 1, 2, 3; end

You sure, that this is the code you use?

def f; 1, 2, 3; end

SyntaxError: compile error
(irb):49: syntax error
def f; 1, 2, 3; end
         ^
        from (irb):49
        from (null):0

I'll use

def f; return 1, 2, 3; end

=> nil

   x,y,z = f
give x=1, y=2, z=3

While
   x = f
gives x=[1, 2, 3]?

It's an optimization to save typing: if there are multiple values and on one side there is only one value "*" is automatically prepended:

x = f

=> [1, 2, 3]

x

=> [1, 2, 3]

x = *f

=> [1, 2, 3]

x

=> [1, 2, 3]

If you want just the first element you got to do

x, = f

=> [1, 2, 3]

x

=> 1

Also:
   x,y=[1, 2], 3 #=> x=[1,2] and y[3], fine
   x,y=[1, 2] #=> x=1, y=2 : not fine

This is inconsistent. Instead I propose:
   x,y,z = f #=> x=1,y=2,z=3 (as current)
   x = f #=> x = 1 (CHANGE)
   *x = f #=> x=[1, 2, 3] (CHANGE)
   x, y = *f #=> an error (f on R.h.s. is not an array)
   x, y, z = [1, 2, 3], 4, 5 #=> x=[1, 2, 3], y=4, z=5 (current)
   x, y, z = [1, 2, 3] #=> x=[1, 2, 3], y=nil, z=nil (CHANGE)
   *x = [1, 2, 3] #=> x = [[1, 2, 3]] (current)
   x, y, z = *[1, 2, 3] #=> x=1, y=2, z=3 (current)

I am trying to write a getter that returns the instance variable + something
special if that instance variable has not been initialized. Normal calls
would ignore the second return value. The behavior above makes it difficult.
Alternatives? Will this continue in 2.0?

Thanks.

btw: I think arrays and multiple values should be strictly separated
syntactic classes.

IMHO we have to judge the costs of this change (broken code, confused coders :-)) vs. what we gain (more consistency). Dunno on what side the balance will come down though.

Kind regards

    robert

Robert Klemme wrote:

"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:WkJDd.9898$ho.9719@fe2.texas.rr.com...
> Why does
> def f; 1, 2, 3; end

You sure, that this is the code you use?

>> def f; 1, 2, 3; end
SyntaxError: compile error
(irb):49: syntax error
def f; 1, 2, 3; end
         ^
        from (irb):49
        from (null):0

I'll use

>> def f; return 1, 2, 3; end
=> nil

> x,y,z = f
> give x=1, y=2, z=3
>
> While
> x = f
> gives x=[1, 2, 3]?

You can think of it as return splat. Looks like Ruby 2.0 will give you
piece of mind, see below...

It's an optimization to save typing: if there are multiple values and

on one

side there is only one value "*" is automatically prepended:

>> x = f
=> [1, 2, 3]
>> x
=> [1, 2, 3]
>> x = *f
=> [1, 2, 3]
>> x
=> [1, 2, 3]

If you want just the first element you got to do

>> x, = f
=> [1, 2, 3]
>> x
=> 1

(...)

I think this is the same issue as

def yields
yield(1,2)
end

=> nil

yields { |a| puts a.inspect }

(irb):4: warning: multiple values for a block parameter (2 for 1)
from (irb):2
[1, 2]
=> nil

More info on this here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/23568

I know there is an English version of that post somewhere, but I can't
find it on google...

-Charlie

But this is about more than just assignment, it is also about method calls
vs. method defs.

> Why does
> def f; 1, 2, 3; end

You sure, that this is the code you use?

My mistake, it was: def f; return 1, 2, 3; end

It's an optimization to save typing: if there are multiple values and on

one

side there is only one value "*" is automatically prepended:

Ah. Unfortunate decision, because ...

x = 1, 2, 3 #=> x = [1, 2, 3]
*x = 1, 2, 3 #=> x = [1, 2, 3]

def f(x); puts x; end
    f(1, 2, 3) #=> ArgError: wrong num. of args
    f([1, 2, 3]) #=> [1, 2, 3]

def g(*x); puts x; end
    g(1, 2, 3) #=> [1, 2, 3]

>> x, = f

Ouch! I did not know that. But
    def f(x,); end #=> error.
i.e. There is no such ',' syntax for method or proc parameters, just '*'.

Method calls establish bindings. *, **, and & are nicely consistent and
symmetric in
    method call: ... f(*x)
        vs.
    method definition: def f(*x); end
Since both method call and assignment establish variable bindings, I would
suggest that:
* on lhs vs. rhs of assignement should parallel * on call vs. def
** on lhs vs. rhs of assignment (if allowed) should parallel ** on call vs.
def
I'd say the same for '&', but can't think of a meaningful '&x = foo'.

IMHO we have to judge the costs of this change (broken code, confused

coders

:-)) vs. what we gain (more consistency). Dunno on what side the balance
will come down though.

Imho, a design decision that saves typing a single *, and as a result makes
binding variables via the assigment operation inconsistent with binding
variables via a method call, is one that _should_ be changed for 2.0.

What do other Rubyists think of this proposal (assuming there was no issue
of backward compatibility) ?

···

"Robert Klemme" <bob.news@gmx.net> wrote

"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:sskEd.20122$q4.3165@fe1.texas.rr.com...

But this is about more than just assignment, it is also about method

calls

vs. method defs.

> > Why does
> > def f; 1, 2, 3; end
>
> You sure, that this is the code you use?

My mistake, it was: def f; return 1, 2, 3; end

> It's an optimization to save typing: if there are multiple values and

on

one
> side there is only one value "*" is automatically prepended:

Ah. Unfortunate decision, because ...

x = 1, 2, 3 #=> x = [1, 2, 3]
*x = 1, 2, 3 #=> x = [1, 2, 3]

def f(x); puts x; end
    f(1, 2, 3) #=> ArgError: wrong num. of args
    f([1, 2, 3]) #=> [1, 2, 3]

def g(*x); puts x; end
    g(1, 2, 3) #=> [1, 2, 3]

> >> x, = f

Ouch! I did not know that. But
    def f(x,); end #=> error.
i.e. There is no such ',' syntax for method or proc parameters, just

'*'.

Yeah:

def f(x,*) p x end

=> nil

f 1,2,3

1
=> nil

Method calls establish bindings. *, **, and & are nicely consistent and
symmetric in
    method call: ... f(*x)
        vs.
    method definition: def f(*x); end
Since both method call and assignment establish variable bindings, I

would

suggest that:
* on lhs vs. rhs of assignement should parallel * on call vs. def
** on lhs vs. rhs of assignment (if allowed) should parallel ** on call

vs.

def

I think "**" is already taken:

-2 ** 3

=> -8

Dunno whether it would be possible to extend the parser with a unary
variant of it.

I'd say the same for '&', but can't think of a meaningful '&x = foo'.

> IMHO we have to judge the costs of this change (broken code, confused
coders
> :-)) vs. what we gain (more consistency). Dunno on what side the

balance

> will come down though.

Imho, a design decision that saves typing a single *, and as a result

makes

binding variables via the assigment operation inconsistent with binding
variables via a method call, is one that _should_ be changed for 2.0.

I'm not sure whether I agree. After all assignment and method call are
not exactly the same although I can see the common issue of variable
binding. One of the differences is that the assignment has a result:

a,b,*c=1,2,3,4

=> [1, 2, 3, 4]

x=(a,b,*c=1,2,3,4)

=> [1, 2, 3, 4]

x

=> [1, 2, 3, 4]

What do other Rubyists think of this proposal (assuming there was no

issue

of backward compatibility) ?

If it wasn't for the backward compatibility issue I think a change would
lead to more consistence. At least nothing I see at the moment.

Regards

    robert

···

"Robert Klemme" <bob.news@gmx.net> wrote